‘new’를 사용하면 왜 메모리 누수가 발생합니까? 비슷하지 않습니다. 이

먼저 C #을 배웠으며 이제 C ++로 시작합니다. 내가 이해하는 것처럼 newC ++의 연산자 는 C #의 연산자 와 비슷하지 않습니다.

이 샘플 코드에서 메모리 누수의 원인을 설명 할 수 있습니까?

class A { ... };
struct B { ... };

A *object1 = new A();
B object2 = *(new B());


답변

무슨 일이야

쓰면 자동 저장 시간을 가진 T t;유형의 객체를 생성합니다 . 범위를 벗어나면 자동으로 정리됩니다.T

쓸 때 동적 저장 시간을 가진 new T()유형의 객체를 생성합니다 . 자동으로 정리되지 않습니다.T

delete정리하려면 포인터를 전달해야 합니다.

그러나 두 번째 예는 더 나쁩니다. 포인터를 역 참조하고 객체를 복사하는 것입니다. 이렇게하면으로 만든 객체에 대한 포인터를 잃을 new수 있으므로 원하는 경우에도 삭제할 수 없습니다!

해야 할 일

자동 저장 기간을 선호해야합니다. 새로운 객체가 필요합니다.

A a; // a new object of type A
B b; // a new object of type B

동적 저장 기간이 필요한 경우 할당 된 객체에 대한 포인터를 자동 저장 기간 객체에 저장하면 자동으로 삭제됩니다.

template <typename T>
class automatic_pointer {
public:
    automatic_pointer(T* pointer) : pointer(pointer) {}

    // destructor: gets called upon cleanup
    // in this case, we want to use delete
    ~automatic_pointer() { delete pointer; }

    // emulate pointers!
    // with this we can write *p
    T& operator*() const { return *pointer; }
    // and with this we can write p->f()
    T* operator->() const { return pointer; }

private:
    T* pointer;

    // for this example, I'll just forbid copies
    // a smarter class could deal with this some other way
    automatic_pointer(automatic_pointer const&);
    automatic_pointer& operator=(automatic_pointer const&);
};

automatic_pointer<A> a(new A()); // acts like a pointer, but deletes automatically
automatic_pointer<B> b(new B()); // acts like a pointer, but deletes automatically

이것은 잘 설명되지 않은 RAII ( 자원 획득이 초기화 ) 라는 일반적인 관용구입니다 . 정리가 필요한 리소스를 확보하면 자동 저장 기간이 걸리는 리소스에 고정되므로 정리에 대해 걱정할 필요가 없습니다. 이것은 모든 리소스, 메모리, 파일 열기, 네트워크 연결 또는 당신이 좋아하는 모든 것에 적용됩니다.

automatic_pointer것은 이미 다양한 형태로 존재합니다. 예를 들어 제공했습니다. 라는 표준 라이브러리에 매우 유사한 클래스가 있습니다 std::unique_ptr.

오래된 C ++ 11 이전 버전도 auto_ptr있지만 이상한 복사 동작이 있기 때문에 더 이상 사용되지 않습니다.

그리고 std::shared_ptr같은 객체에 대한 여러 포인터를 허용하고 마지막 포인터가 파괴 될 때만 정리하는 더 똑똑한 예제 가 있습니다.


답변

단계별 설명 :

// creates a new object on the heap:
new B()
// dereferences the object
*(new B())
// calls the copy constructor of B on the object
B object2 = *(new B());

따라서 이것의 끝에는 포인터가없는 힙에 객체가 있으므로 삭제할 수 없습니다.

다른 샘플 :

A *object1 = new A();

delete할당 된 메모리 를 잊어 버린 경우에만 메모리 누수입니다 .

delete object1;

C ++에는 자동 스토리지가있는 오브젝트, 스택에서 자동으로 처리 된 오브젝트 및 힙에 동적 스토리지가있는 오브젝트 newdelete있습니다. (이것은 모두 대략 넣어집니다)

delete할당 된 모든 객체에 대해 있어야한다고 생각하십시오 new.

편집하다

생각해 object2보면 메모리 누수가 필요하지 않습니다.

다음 코드는 단지 포인트를 만들기위한 것입니다. 나쁜 생각입니다. 이런 코드를 좋아하지 마십시오.

class B
{
public:
    B() {};   //default constructor
    B(const B& other) //copy constructor, this will be called
                      //on the line B object2 = *(new B())
    {
        delete &other;
    }
}

이 경우 other참조로 전달 되므로 가 가리키는 정확한 객체가됩니다 new B(). 따라서 주소를 가져오고 &other포인터를 삭제하면 메모리가 해제됩니다.

그러나 나는 이것을 충분히 강조 할 수 없습니다. 요점은 바로 여기에 있습니다.


답변

두 개의 “객체”가 주어지면 :

obj a;
obj b;

메모리에서 동일한 위치를 차지하지 않습니다. 다시 말해,&a != &b

하나의 값을 다른 값으로 지정해도 위치는 변경되지 않지만 내용은 변경됩니다.

obj a;
obj b = a;
//a == b, but &a != &b

직관적으로 포인터 “객체”는 같은 방식으로 작동합니다.

obj *a;
obj *b = a;
//a == b, but &a != &b

이제 예제를 보자.

A *object1 = new A();

이 값을 new A()에 할당하고 object1있습니다. 값은을 의미하는 포인터 object1 == new A()이지만 &object1 != &(new A()). (이 예제는 유효한 코드가 아니며 설명만을위한 것입니다)

포인터의 값이 유지되기 때문에 포인터가 가리키는 메모리를 해제 할 수 있습니다. delete object1;규칙으로 인해 delete (new A());누수가없는 것과 동일하게 동작합니다 .


두 번째 예에서는 뾰족한 개체를 복사합니다. 값은 실제 포인터가 아니라 해당 객체의 내용입니다. 다른 모든 경우와 마찬가지로 &object2 != &*(new A()).

B object2 = *(new B());

할당 된 메모리에 대한 포인터를 잃어 버렸으므로 해제 할 수 없습니다. delete &object2;작동하는 것처럼 보일 수 있지만에 &object2 != &*(new A())해당 delete (new A())하지 않으므로 유효하지 않습니다.


답변

C # 및 Java에서는 new를 사용하여 모든 클래스의 인스턴스를 생성 한 다음 나중에 삭제하지 않아도됩니다.

C ++에는 객체를 만드는 키워드 “new”도 있지만 Java 나 C #과 달리 객체를 만드는 유일한 방법은 아닙니다.

C ++에는 객체를 생성하는 두 가지 메커니즘이 있습니다.

  • 자동적 인
  • 동적

자동 작성을 사용하면 범위가 지정된 환경에서-함수에서 또는-클래스 (또는 구조체)의 멤버로 오브젝트를 작성합니다.

함수에서는 다음과 같이 생성합니다.

int func()
{
   A a;
   B b( 1, 2 );
}

클래스 내에서 일반적으로 다음과 같이 생성합니다.

class A
{
  B b;
public:
  A();
};

A::A() :
 b( 1, 2 )
{
}

첫 번째 경우, 스코프 블록이 종료되면 오브젝트가 자동으로 삭제됩니다. 이것은 함수 또는 함수 내의 스코프 블록 일 수 있습니다.

후자의 경우, 객체 b는 객체 인 A의 인스턴스와 함께 파괴된다.

객체의 수명을 제어해야 할 때 객체에 새로운 객체가 할당 된 다음 객체를 삭제하려면 삭제가 필요합니다. RAII라는 기술을 사용하면 자동 객체 내에 넣어 객체를 생성하는 시점에서 객체 삭제를 처리하고 해당 자동 객체의 소멸자가 적용되기를 기다립니다.

그러한 객체 중 하나는 “삭제 자”로직을 호출하지만 객체를 공유하는 shared_ptr의 모든 인스턴스가 파괴 된 경우에만 shared_ptr입니다.

일반적으로 코드에 새로운 호출이 많이있을 수 있지만 삭제 호출은 제한적이어야하며 항상 스마트 포인터에 넣은 소멸자 또는 “삭제 자”객체에서 호출해야합니다.

소멸자는 절대 예외를 던져서는 안됩니다.

이렇게하면 메모리 누수가 거의 없습니다.


답변

B object2 = *(new B());

이 라인은 누출의 원인입니다. 이것을 조금 골라 보자 ..

object2는 주소 1에 저장된 B 유형의 변수입니다 (예, 임의의 숫자를 선택합니다). 오른쪽에서, 당신은 새로운 B 또는 B 타입의 객체에 대한 포인터를 요구했습니다. 프로그램은 기꺼이 당신에게 그것을주고 주소 2에 새로운 B를 할당하고 주소 3에 포인터를 만듭니다. 주소 2의 데이터에 액세스 할 수있는 유일한 방법은 주소 3의 포인터를 사용 *하는 것입니다. 다음으로 포인터가 가리키는 데이터 (주소 2의 데이터)를 얻기 위해 포인터를 역 참조했습니다 . 이렇게하면 해당 데이터의 복사본이 효과적으로 생성되어 주소 1에 할당 된 object2에 할당됩니다. 원본이 아니라 사본임을 기억하십시오.

자, 여기 문제가 있습니다 :

실제로 사용할 수있는 위치에 포인터를 저장하지 않았습니다! 이 할당이 완료되면 포인터 (주소 2에 액세스하는 데 사용 된 주소 3의 메모리)가 범위를 벗어났습니다. 더 이상 delete를 호출 할 수 없으므로 address2의 메모리를 정리할 수 없습니다. 남아있는 것은 address1의 address2에서 데이터 사본입니다. 같은 것들 중 두 가지는 메모리에 앉아 있습니다. 하나는 액세스 할 수 있고 다른 하나는 액세스 할 수 없습니다 (경로를 잃었 기 때문에). 이것이 메모리 누수 인 이유입니다.

C # 배경에서 C ++의 포인터가 어떻게 작동하는지 많이 읽으라고 제안합니다. 그것들은 고급 주제이며 이해하는 데 시간이 걸릴 수 있지만 그 사용법은 매우 중요합니다.


답변

더 쉬워지면 컴퓨터 메모리를 호텔처럼 생각하고 프로그램은 필요할 때 방을 고용하는 고객입니다.

이 호텔이 작동하는 방식은 방을 예약하고 떠날 때 포터에게 알리는 것입니다.

포터에게 알리지 않고 방을 예약하고 떠나면 포터는 그 방이 여전히 사용 중이라고 생각하고 다른 사람이 그 방을 사용하지 못하게합니다. 이 경우 공간 누출이 있습니다.

프로그램이 메모리를 할당하고 삭제하지 않으면 (단지 사용을 중지) 컴퓨터는 메모리가 여전히 사용 중이라고 생각하고 다른 사람이 메모리를 사용할 수 없게합니다. 이것은 메모리 누수입니다.

이것은 정확한 비유는 아니지만 도움이 될 수 있습니다.


답변

생성 object2할 때 새로 생성 한 객체의 복사본을 생성하지만 할당되지 않은 포인터도 손실되므로 나중에 삭제할 수있는 방법이 없습니다. 이를 피하려면 object2참조를해야합니다.