문제

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

Dog* sparky = new Dog();

나중에 당신은 할 것임을 암시합니다.

delete sparky;

의미가 있습니다. 이제 동적 메모리 할당이 불필요한 경우 위의 대신 위를 사용할 이유가 있습니까?

Dog sparky;

그리고 Sparky가 범위를 벗어나면 파괴자를 부르십시오.

감사!

도움이 되었습니까?

해결책

반대로, 당신은 항상 스택 할당을 선호해야하며, 경험의 규칙에 따라 사용자 코드에 새/삭제가 없어야합니다.

당신이 말했듯이, 변수가 스택에 선언 될 때, 스코프가 나올 때 파괴자는 자동으로 호출되며, 이는 자원 수명을 추적하고 누출을 피하기위한 주요 도구입니다.

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

이 관용구의 가장 일반적인 이름은입니다 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"를 인쇄합니다. 이제 스택을 사용할 때 어떤 일이 발생하는지 보자.

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

이것은 "A"를 인쇄 할 것이며, 이는 Java 또는 다른 객체 지향 언어에 익숙한 사람들에게 직관적이지 않을 수 있습니다. 그 이유는 인스턴스에 대한 포인터가 없기 때문입니다. B 더 이상. 대신, 인스턴스 B 생성되고 복사됩니다 a 유형의 변수 A.

특히 C ++를 처음 접할 때 어떤 일이 의도적으로 발생할 수 있습니다. C에는 당신이 당신의 포인터를 가지고 있습니다. 당신은 그것들을 사용하는 방법을 알고 있으며 그들은 항상 동일합니다. C ++에서는 그렇지 않습니다. 이 예제에서 방법에 대한 논쟁으로 사용하면 어떤 일이 일어나는지 상상해보십시오. a 유형입니다 A 또는 A* 또는 A& (Call-by-Reference). 많은 조합이 가능하며 모두 다르게 행동합니다.

글쎄, 포인터를 사용하는 이유는 Malloc과 함께 할당 된 C에서 포인터를 사용하는 이유와 정확히 동일합니다.

피할 수있는 경우 새 연산자를 사용하지 않는 것이 좋습니다. 특히 예외를 사용하는 경우. 일반적으로 컴파일러에 객체를 풀어주는 것이 훨씬 안전합니다.

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

void FeedTheDog(Dog* hungryDog);

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

Dog goodDog;
FeedTheDog(&goodDog);

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

(1). 예외가 발생할 경우 스택 풀기에 대해 걱정할 필요가 없습니다.

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

내가 걱정하는 유일한 이유는 개가 이제 더미가 아닌 스택에 할당되기 때문입니다. 개가 크기가 메가 바이트 인 경우 문제가있을 수 있습니다.

새/삭제 경로로 가야하는 경우 예외를 조심하십시오. 이 때문에 Auto_Ptr 또는 Boost 스마트 포인터 유형 중 하나를 사용하여 객체 수명을 관리해야합니다.

스택에 할당 할 수있을 때 (힙에) 새로운 (힙에) 할 이유가 없습니다 (어떤 이유로 든 작은 스택이없고 힙을 사용하고 싶지 않다면.

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

다른 사람이 언급하지 않은 추가 이유가 있습니다. 왜 객체를 동적으로 만들도록 선택할 수 있습니다. 동적 인 힙 기반 물체를 사용하여 사용할 수 있습니다. 다형성.

Visual Studio에서도 같은 문제가있었습니다. 사용해야합니다.

yourclass-> classMethod ();

대신 :

yourclass.classMethod ();

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top