태그 보관물: instantiation

instantiation

C ++ 개체 인스턴스화 이해하려는 C 프로그래머입니다. 많은 튜토리얼은 다음과 같은

저는 C ++를 이해하려는 C 프로그래머입니다. 많은 튜토리얼은 다음과 같은 스 니펫을 사용하여 객체 인스턴스화를 보여줍니다.

Dog* sparky = new Dog();

이는 나중에 다음을 수행 할 것임을 의미합니다.

delete sparky;

말이 되네요. 이제 동적 메모리 할당이 필요하지 않은 경우 대신 위를 사용하는 이유가 있습니까?

Dog sparky;

Sparky가 범위를 벗어나면 소멸자가 호출되도록할까요?

감사!



답변

반대로, 경험상 사용자 코드에 새 / 삭제가 없어야하는 한 항상 스택 할당을 선호해야합니다.

말했듯이, 변수가 스택에서 선언되면 해당 소멸자가 범위를 벗어날 때 자동으로 호출되며, 이는 리소스 수명을 추적하고 누수를 방지하기위한 주요 도구입니다.

따라서 일반적으로 메모리 (new 호출), 파일 핸들, 소켓 또는 기타 모든 리소스를 할당해야 할 때마다 생성자가 리소스를 획득하고 소멸자가이를 해제하는 클래스로 래핑합니다. 그런 다음 스택에 해당 유형의 개체를 만들 수 있으며 리소스가 범위를 벗어날 때 리소스가 해제되도록 보장됩니다. 이렇게하면 메모리 누수를 방지하기 위해 모든 곳에서 새 / 삭제 쌍을 추적 할 필요가 없습니다.

이 관용구의 가장 일반적인 이름은 RAII입니다.

또한 전용 RAII 개체 외부에 새로운 항목을 할당해야하는 드문 경우에 결과 포인터를 래핑하는 데 사용되는 스마트 포인터 클래스를 살펴보십시오. 대신 포인터를 스마트 포인터에 전달하면, 예를 들어 참조 카운트를 통해 수명을 추적하고 마지막 참조가 범위를 벗어날 때 소멸자를 호출합니다. 표준 라이브러리는 std::unique_ptr단순한 범위 기반 관리를 위해 있으며 std::shared_ptr공유 소유권을 구현하기 위해 참조 계산을 수행합니다.

많은 튜토리얼은 다음과 같은 스 니펫을 사용하여 객체 인스턴스화를 보여줍니다.

그래서 당신이 발견 한 것은 대부분의 튜토리얼이 형편 없다는 것입니다. 😉 대부분의 튜토리얼은 필요하지 않을 때 변수를 생성하기 위해 new / delete를 호출하고 할당 수명을 추적하는 데 어려움을주는 것을 포함하여 형편없는 C ++ 사례를 가르칩니다.


답변

스택에있는 것이 할당 및 자동 해제 측면에서 이점이 될 수 있지만 몇 가지 단점이 있습니다.

  1. 스택에 거대한 개체를 할당하고 싶지 않을 수 있습니다.

  2. 다이나믹 파견! 이 코드를 고려하십시오.

#include <iostream>

class A {
public:
  virtual void f();
  virtual ~A() {}
};

class B : public A {
public:
  virtual void f();
};

void A::f() {cout << "A";}
void B::f() {cout << "B";}

int main(void) {
  A *a = new B();
  a->f();
  delete a;
  return 0;
}

그러면 “B”가 인쇄됩니다. 이제 Stack을 사용할 때 어떤 일이 발생하는지 살펴 보겠습니다.

int main(void) {
  A a = B();
  a.f();
  return 0;
}

이렇게하면 “A”가 인쇄되며 Java 또는 기타 객체 지향 언어에 익숙한 사용자에게는 직관적이지 않을 수 있습니다. 그 이유는 B더 이상 인스턴스에 대한 포인터가 없기 때문입니다 . 대신의 인스턴스 B가 생성되고 a유형의 변수에 복사됩니다 A.

특히 C ++를 처음 접할 때 어떤 일이 직관적이지 않게 발생할 수 있습니다. C에서는 포인터가 있고 그게 전부입니다. 당신은 그것들을 사용하는 방법을 알고 있으며 그들은 항상 동일합니다. C ++에서는 그렇지 않습니다. 그냥 당신이하는 방법에 대한 인수로이 예에서 사용할 때, 무슨 상상 – 일을 더 복잡하게 얻을 경우는 큰 차이를 만들 않습니다 a유형 인 A또는 A*심지어 A&(통화 별 참조). 많은 조합이 가능하며 모두 다르게 작동합니다.


답변

글쎄요, 포인터를 사용하는 이유는 malloc으로 할당 된 C에서 포인터를 사용하는 이유와 똑같을 것입니다 : 객체가 변수보다 오래 살기를 원한다면!

피할 수 있다면 new 연산자를 사용하지 않는 것이 좋습니다. 특히 예외를 사용하는 경우. 일반적으로 컴파일러가 개체를 해제하도록하는 것이 훨씬 안전합니다.


답변

나는 운영자의 & 주소를 알지 못하는 사람들로부터이 안티 패턴을 보았다. 포인터가있는 함수를 호출해야하는 경우 항상 힙에 할당하여 포인터를 얻습니다.

void FeedTheDog(Dog* hungryDog);

Dog* badDog = new Dog;
FeedTheDog(badDog);
delete badDog;

Dog goodDog;
FeedTheDog(&goodDog);


답변

힙을 매우 중요한 부동산으로 취급하고 매우 신중하게 사용하십시오. 기본 규칙은 가능할 때마다 스택을 사용 하고 다른 방법이 없을 때마다 힙을 사용하는 것입니다. 스택에 객체를 할당하면 다음과 같은 많은 이점을 얻을 수 있습니다.

(1). 예외의 경우 스택 해제에 대해 걱정할 필요가 없습니다.

(2). 힙 관리자가 필요로하는 것보다 더 많은 공간을 할당하여 발생하는 메모리 조각화에 대해 걱정할 필요가 없습니다.


답변

내가 걱정할 유일한 이유는 Dog가 이제 힙이 아닌 스택에 할당된다는 것입니다. 따라서 Dog의 크기가 메가 바이트라면 문제가있을 수 있습니다.

새 / 삭제 경로로 이동해야하는 경우 예외에주의하십시오. 이 때문에 auto_ptr 또는 boost 스마트 포인터 유형 중 하나를 사용하여 개체 수명을 관리해야합니다.


답변

스택에 할당 할 수있을 때 (힙에) 새로 만들 이유가 없습니다 (어떤 이유로 든 작은 스택이 있고 힙을 사용하려는 경우가 아니라면).

힙에 할당하려는 경우 표준 라이브러리에서 shared_ptr (또는 그 변형 중 하나) 사용을 고려할 수 있습니다. shared_ptr에 대한 모든 참조가 존재하지 않으면 삭제 작업을 처리합니다.