2011-01-23 3 views
4

나는 C++ Primer Plus by Stephen Frata을 읽습니다. 저는 6 장까지 읽었습니다. 즉, 포인터에 대해서 알았지 만 객체와 클래스에 대해서는 알지 못했습니다 (OOP에 대해 알고 있긴하지만).스마트 포인터가없는 포인터를 처리하는 방법은 무엇입니까?

ActionScript (Flash) 및 Java 배경에서 왔기 때문에 이전에는 포인터를 다루지 않았지만 이해합니다. 나는 그들에 관해 많은 질문을 가지고있다.

필자가 이해 한 것처럼 새로 만들기와 삭제를 쌍으로해야합니다. 즉, 포인터를 만드는 객체/함수가 그것을 해제해야합니다. 그러나 다음과 같은 단순한 팩토리 함수를 상상해보십시오.

SomeObject * createSomeObject(){ 
    return new SomeObject; 
} 

상당히 문제가있는 것처럼 보입니다. 이 포인터를 누가 지금 비우는가?

내가 만든 포인터에 공개 액세스 권한을 부여하는 클래스를 만드는 경우 어떻게됩니까? 새/삭제 규칙 다음에이 클래스는 소멸자에서 포인터를 해제해야합니다. 그러나 포인터가 다른 클래스에 의해 사용되었을 수 있기 때문에 첫 번째 클래스를 파기하면 두 번째 클래스가 파손됩니다.

두 개의 심문은 비슷합니다. 포인터를 만든 사람이 아닌 다른 사람이 알고있는 포인터를 관리하려면 어떻게해야합니까?

참고 : 스마트 포인터가이 문제를 해결할 수 있다는 것을 알고 있지만 사람들이 없이는 어떻게하는지 궁금합니다.

+4

'참고 : 스마트 포인터가이 문제를 해결할 수 있다는 것을 알고 있지만, 사람들이 그것 없이는 어떻게하는지 궁금합니다. '- 한마디로 말하면. :) – etarion

+2

당신이 가지고있는 책이 해로울 수 있기 때문에 다른 책을 구입하고 싶을 수도 있습니다. http://accu.org/index.php?module=bookreviews&func=search&rid=1744 – sellibitze

+0

@sellibitze 내 자바 배경이 주어지면 어떤 제안을하니? – subb

답변

1

상당히 문제가있는 것처럼 보입니다. 이 포인터를 해제하는 데 책임이있는 사람은 누구입니까? ?

나는 쌍 기능을 제안합니다.

Xyz* CreateXyz(); 
void DestroyXyz(Xyz *xyz); 


Abc* NewAbc(); 
void DeleteAbc(Abc *abc); 

또는 당신은 단순히 클라이언트, 즉 또한 사용 후 반환 된 객체에 삭제해야 함수를 호출들에 Xyz/Abc을 삭제하는 responsibilty를 전송할 수 있습니다.

무엇을 선택하든 작성한 개체를 어떻게 파기해야하는지 문서에서 명확히하십시오.

특히 내가 삭제하기 전에 고려해야 할 사항이 많은 경우 쌍 기능이 더 좋습니다!

편집 : 당신은 DLL 또는 일부 동적 라이브러리를 빌드 할 때 나는이 쌍-기능 접근 방식을 제안한다. 실제로 이것은 객체가 생성 된 메모리 풀에서 파괴된다는 것을 보장합니다! 아무도 없습니다

+0

RAII를 사용하는 것이 잘못된 이유는 무엇입니까? – jalf

+0

DestroyXyz (xyz) 호출 방법; delete xyz와 다른; "DestroyXyz()를 호출하는 사람"과 같은 문제는 동일하게 유지됩니다. – Tonttu

+1

IMO 이것은 스마트 포인터보다 좋지 않습니다. 해당 DestroyXyz 또는 DeleteAbc 호출을 잊어 버릴 수 있기 때문입니다. 편집과 함께 – AshleysBrain

0

who is responsible for creating/destroying. 당신은 논리를 정의합니다.

128 바이트의 긴 문자열을 저장하기 위해 포인터를 512 바이트에 할당하고 문자열을 성공적으로 변경하여 파일에 저장 한 경우 언제든지 포인터를 삭제할 수 있습니다.

2

개체의 "소유권"과 수명은 메모리 관리 문제와 C++의 디자인에 큰 영향을줍니다. 일반적으로 스마트 포인터 및 유사한 기술이 선호됩니다.

그러나 스마트 포인터 등을 사용하고 싶지 않으면 매우 엄격해야합니다. 일반적으로 특정 객체의 메모리 관리는 하나의 인터페이스에서 발생해야합니다. 따라서 힙 기반 객체 (예 : createSomeObject())를 만드는 모든 함수에는 객체를 삭제하는 일치 함수가 있어야합니다 (예 : deleteSomeObject(SomeObject *)). 물론 이런 종류의 지침에는 항상 예외가 있습니다.

이 문서와 좋은 문서는 누군가가 망쳐 놓아 메모리 누수가 발생할 가능성을 최소화합니다.

0

C++에서 "포인터를 만드는 객체/함수가 그것을 해제해야합니다."라고 항상 말하는 것은 아닙니다. 클래스 팩토리에서, 객체를 사용하는 코드는 포인터의 추가 사용자가 없도록 을 삭제합니다. 그렇지 않으면 스마트 포인터 또는 참조 카운팅 카운트가 필요합니다.

0

일반적으로 팩토리 함수는 생성 한 객체를 삭제할 책임이 없습니다. 새 클래스/삭제 클래스를 하나의 클래스에 포함 시키면 특히 멀티 스레드 환경에서 포인터를 반환하는 것은 사실상 불가능합니다.

대부분의 경우 문서/참조는 명확하지 않은 경우 개체를 삭제할 책임이있는 사람을 명시 적으로 지정합니다. 이것을 수동으로 처리하지 않고 가능한 해결책은 내가 생각하기에 가비지 수집 (garbage collection) 분야로 들어가게 될 것이다 (참조 카운터는 대중적인 접근법이다).

0

모든 상황에서 작동 할 수있는 간단한 대답은 없습니다. 규칙에는 항상 예외가 있습니다. 소프트웨어에 대한 다른 아키텍처를 고려하고 코드를 읽는 사람에게 가장 쉽게 이해할 수있는 방법을 결정해야합니다.

3

"누가 삭제해야합니까?" 아주 좋은 질문입니다. 일반적으로 이와 같은 함수를 사용하면 반환 된 포인터를 삭제해야한다고 간단히 문서화합니다. 책임있는 클래스 또는 기능을 결정하는 것은 공장 사용자가 결정합니다. 그러나 이것은 다소 모호하며 실제로 문제입니다.

현대적인 C++ 스타일에서 이것은 스마트 포인터가 사용되는 이유입니다. 고려 :

std::unique_ptr<SomeObject> createSomeObject() { 
    return new SomeObject; 
} 

를이 경우, 포인터가 반환 unique_ptr가 소유하고 있습니다. 당신이 그것을 움직일 때마다, 저장된 포인터는 소멸자 (소멸자를 포함하는 객체가 소멸 될 때)에서 삭제됩니다. 이것은 코드의 어느 부분이 코드를 파기 할 책임이 있는지를 분명히하고 삭제가 자동으로 발생하기 때문에 (삭제하거나 "파괴"호출하는 것을 잊을 수는 없습니다) 위의 문제에 대한 해결책으로 간주됩니다.

하나는 힙에 할당하지 않는다 "입니다 :

2

이 서로 다른 접근 방식이에있는 대신이 작업을 수행 할 경우, 하나의 개체를 무료로 없습니다

SomeObject createSomeObject(){ 
    return SomeObject(); 
} 

, 그리고 당신은 어떤 포인터도 걱정할 필요가 없습니다. 잠재적 인의 단점은 SomeObject이 복사 가능해야한다는 것입니다. 그러나 이것은 종종 좋은 해결책이며 일반적으로 사용자 기본값이되어야합니다. 사용자 코드에 new/delete을 사용하지 말고 생성자/소멸자 호출 (예 :일 수 있습니다.은 힙에 내부적으로 일부 데이터를 할당하고 객체 자체가 파괴되면 해제합니다.

두 번째 방법은 관련이 있지만, 스마트 포인터를 사용

std::shared_ptr<SomeObject> createSomeObject(){ 
    return std::make_shared(new SomeObject()); 
} 

이 당신이 포인터를 반환하지 않는 점에서 유사하다, 당신은 필요 아무것도 삭제 담당하는 객체를 반환하고 삭제됩니다. 스마트 포인터가 SomeObject 인스턴스의 소유권을 가져 왔으며 적절한 경우 삭제합니다.

상황에 따라 std::auto_ptr 또는 std::unique_ptr이 바람직 할 수 있습니다.

두 경우 모두, 모든 C++ 프로그래머가 알아야 할 매우 강력한 숙어 RAII을 사용하고 있습니다. 리소스는 으로 항상으로 적절히 복사되고 이동되며 내부 리소스를 정리해야하는 로컬 (힙 할당이 아닌) 객체로 래핑되어야합니다.

0

Naked new & 삭제가있는 경우 기본적으로 C++에서 예외 안전 코드를 작성할 수 없습니다. 할당 된 자원을 래핑하는

  • 스마트 포인터 :

    그래서, 당신은 기본적으로 두 가지 선택이있다.

  • 레퍼런스 카운팅 래퍼 클래스 관용구를 중심으로 클래스를 디자인함으로써 알몸의 새로운 & 삭제를 피하십시오. 실제로 이것은 어려운 방법으로 수행 된 스마트 포인터의 또 다른 이름입니다.

기본 아이디어는 프레임 워크에 복사 할 가치가있는 클래스 집합을 만들고 참조 횟수와 복사 비용이 많이 드는 다른 클래스를 래핑하는 것입니다.

싸구려 복사 클래스에 할당 및 복사 연산자를 제공하면 int와 같은 원시 유형과 동일한 의미로 전달 될 수있는 데이터 구조를 얻을 수 있습니다.

스마트 포인터를 다시 발명 한 것 이상이지만 코드에서 non-typedef'ed 스마트 포인터를 사용하는 것보다 구문이 덜 복잡하고 C++ 생성자에 대해 충분히 알고 만족 스럽습니다. 그리고 래퍼를 작성하기위한 연산자 오버로딩.

0

Jalf는 이미 중요한 비트를 언급했습니다. 즉, 반드시 힙에 할당하지 않아도됩니다. 다른

뭔가 이것으로 재생하고,이 책은 아마 당신에게 말하지 않을 것이다 :

C의 ++가 OOP에 특히 좋지 않다. 원래 OOP 목표를 의미하는 클래스를 가진 C로 설계되었지만 OOP는 실제로 C++에서 매우 성가 시며 대부분의 최신 C++ 숙어에 초점을 맞추지 않습니다. 당신은 OPP 프레임 워크에 묶여있는 경우

당신은 기본적으로 두 가지 선택이 있습니다

  • 스마트 포인터, 또는
  • 가비지 컬렉터 (필자는 넓은 의미에서이 말은 - 사용자 정의 (예 : pool) 할당자를 계산할 수도 있습니다).

일반적으로 C++에서 명시 적 포인터 및 프리 저장소 관리없이 수행하려고합니다. 즉, 스택에 비즈니스 개체를 할당하고 RAII를 통해 모든 동적 메모리 관리를 캡슐화합니다.

물론 이것은 하위 유형 다형성을 훨씬 더 어렵게 만듭니다. 그러나 이것을 줄이기 위해 C++은 매우 추상적 인 고급 코드 (OOP보다는 틀림없이)를 작성할 수있는 매우 강력한 템플릿 메커니즘을 제공합니다. 이것은 때로 객체 지향으로부터 오프셋하기 위해 알고리즘 지향적이라고 불린다. C++ <algorithms> 헤더와 표준 라이브러리 컨테이너가 이에 대한 좋은 예입니다.

관련 문제