programing

항상 스마트 포인터를 사용하는 것이 좋은 습관입니까?

nasanasas 2020. 10. 24. 10:22
반응형

항상 스마트 포인터를 사용하는 것이 좋은 습관입니까?


나는 스마트 포인터가 원시 포인터보다 훨씬 더 편하다고 생각합니다. 그렇다면 항상 스마트 포인터를 사용 하는 것이 좋은 생각 입니까? (저는 Java 배경에서 왔기 때문에 명시 적 메모리 관리 개념이별로 마음에 들지 않습니다. 따라서 스마트 포인터에 심각한 성능 문제가없는 한 계속해서 고수하고 싶습니다.)

참고 : 저는 Java 배경에서 왔지만 스마트 포인터의 구현과 RAII의 개념을 잘 이해하고 있습니다. 따라서 답변을 게시 할 때이 지식을 당연한 것으로 받아 들일 수 있습니다. 거의 모든 곳에서 정적 할당을 사용하고 필요할 때만 포인터를 사용합니다. 내 질문은 단지 : 원시 포인터 대신 스마트 포인터를 항상 사용할 수 있습니까 ???


몇 번의 편집을 감안할 때 포괄적 인 요약이 유용 할 것이라는 인상을 받았습니다.

1.하지 않을 때

스마트 포인터를 사용해서는 안되는 두 가지 상황이 있습니다.

첫 번째는 C++실제로 클래스를 사용해서는 안되는 똑같은 상황입니다 . IE : 클라이언트에 소스 코드를 제공하지 않는 경우 DLL 경계. 일화를 말해 보자.

두 번째는 훨씬 더 자주 발생합니다. 똑똑한 관리자는 소유권을 의미 합니다. 수명을 관리하지 않고 포인터를 사용하여 기존 리소스를 가리킬 수 있습니다. 예를 들면 다음과 같습니다.

void notowner(const std::string& name)
{
  Class* pointer(0);
  if (name == "cat")
    pointer = getCat();
  else if (name == "dog")
    pointer = getDog();

  if (pointer) doSomething(*pointer);
}

이 예는 제한적입니다. 그러나 포인터는 유효하지 않은 위치 (널 포인터)를 가리킬 수 있다는 점에서 참조와 의미 상 다릅니다. 이 경우 개체의 수명을 관리하고 싶지 않기 때문에 대신 스마트 포인터를 사용하지 않는 것이 좋습니다.

2. 똑똑한 관리자

똑똑한 관리자 클래스를 작성하지 않는 한 키워드 사용하면 delete 뭔가 잘못된 것입니다.

논란의 여지가있는 관점이지만 결함이있는 코드의 예를 너무 많이 검토 한 후 더 이상 기회를 잡지 않습니다. 따라서 작성 new하는 경우 새로 할당 된 메모리에 대한 스마트 관리자가 필요합니다. 그리고 지금 당장 필요합니다.

당신이 프로그래머가 아니라는 의미는 아닙니다! 반대로, 작업을 반복하는 대신 작동하는 것으로 입증 된 코드를 재사용하는 것이 핵심 기술입니다.

이제 진정한 어려움이 시작됩니다. 어떤 똑똑한 관리자입니까?

3. 스마트 포인터

다양한 특성을 가진 다양한 스마트 포인터가 있습니다.

std::auto_ptr일반적으로 피해야하는 건너 뛰기 (복사 의미가 잘못됨).

  • scoped_ptr: 오버 헤드가 없으며 복사하거나 이동할 수 없습니다.
  • unique_ptr: 오버 헤드 없음, 복사 불가, 이동 가능.
  • shared_ptr/ weak_ptr: 약간의 오버 헤드 (참조 카운팅), 복사 가능.

일반적으로 scoped_ptr또는 unique_ptr. 여러 명의 소유자가 필요한 경우 디자인을 변경하십시오. 디자인을 변경할 수없고 실제로 여러 명의 소유자가 필요한 경우를 사용 shared_ptr하지만 weak_ptr중간 어딘가에을 사용하여 끊어야하는 참조주기에주의 하세요.

4. 스마트 컨테이너

많은 스마트 포인터는 복사 할 수 없으므로 STL 컨테이너와의 사용이 다소 손상됩니다.

shared_ptr오버 헤드 에 의존하는 대신 Boost Pointer Container 의 스마트 컨테이너를 사용하십시오 . 클래식 STL 컨테이너의 인터페이스를 에뮬레이트하지만 소유 한 포인터를 저장합니다.

5. 자신의 롤링

자신의 똑똑한 관리자를 롤링하고 싶을 때가 있습니다. 미리 사용중인 라이브러리의 일부 기능을 놓친 것이 아닌지 확인하십시오.

예외 상황에서 똑똑한 관리자를 작성하는 것은 매우 어렵습니다. 당신은 일반적으로 해당 메모리를 사용할 수있는 가정 할 수 없다 ( new실패 할 수 있습니다) 또는 Copy ConstructorS는이 no throw보증을.

std::bad_alloc예외 를 무시하고 Copy Constructor많은 헬퍼 중 s가 실패하지 않도록 강요하는 것이 다소 허용 될 수 있습니다 ... 결국, 그것이 boost::shared_ptrdeleter D템플릿 매개 변수에 대해하는 일입니다.

그러나 특히 초보자에게는 권장하지 않습니다. 까다로운 문제이며 지금 당장은 버그를 눈치 채지 못할 것입니다.

6. 예

// For the sake of short code, avoid in real code ;)
using namespace boost;

// Example classes
//   Yes, clone returns a raw pointer...
// it puts the burden on the caller as for how to wrap it
//   It is to obey the `Cloneable` concept as described in 
// the Boost Pointer Container library linked above
struct Cloneable
{
  virtual ~Cloneable() {}
  virtual Cloneable* clone() const = 0;
};

struct Derived: Cloneable
{
  virtual Derived* clone() const { new Derived(*this); }
};

void scoped()
{
  scoped_ptr<Cloneable> c(new Derived);
} // memory freed here

// illustration of the moved semantics
unique_ptr<Cloneable> unique()
{
  return unique_ptr<Cloneable>(new Derived);
}

void shared()
{
  shared_ptr<Cloneable> n1(new Derived);
  weak_ptr<Cloneable> w = n1;

  {
    shared_ptr<Cloneable> n2 = n1;          // copy

    n1.reset();

    assert(n1.get() == 0);
    assert(n2.get() != 0);
    assert(!w.expired() && w.get() != 0);
  } // n2 goes out of scope, the memory is released

  assert(w.expired()); // no object any longer
}

void container()
{
  ptr_vector<Cloneable> vec;
  vec.push_back(new Derived);
  vec.push_back(new Derived);

  vec.push_back(
    vec.front().clone()         // Interesting semantic, it is dereferenced!
  );
} // when vec goes out of scope, it clears up everything ;)

Smart pointers do perform explicit memory management, and if you don't understand how they are doing it, you are in for a world of trouble when programming with C++. And remember that memory isn't the only resource that they manage.

But to answer your question you should prefer smart-pointers as a first approximation to a solution, but possibly be prepared to ditch them when necessary. You should never use pointers (or any sort) or dynamic allocation when it can be avoided. For example:

string * s1 = new string( "foo" );      // bad
string s2( "bar" );    // good

Edit: To answer your suplementary question "Can I always use smart pointers in place of raw pointers??? Then, no you can't. If (for example) you need to implement your own version of operator new, you would have to make it return a pointer, not a smart pointer.


Usually you shouldn't use pointers (smart or otherwise) if you don't need them. Better make local variables, class members, vector elements and similar items normal objects instead of pointers to objects. (Since you come from Java you're probably tempted allocate everything with new, which is not recommended.)

This approach ("RAII") saves you from worrying about pointers most of the time.

When you have to use pointers it depends on the situation and why exactly you need pointers, but usually smart pointers can be used. It might not be always (in bold) be the best option, but this depends on the specific situation.


A good time not to use smart pointers, is at the interface boundary of a DLL. You don't know whether other executables will be built with the same compiler/libraries. Your system's DLL calling convention won't specify what standard or TR1 classes look like, smart pointers included.

Within an executable or library, if you want to represent ownership of the pointee, then smart pointers are on average the best way to do it. So it's fine to want to always use them in preference to raw. Whether you actually can always use them is another matter.

For a concrete example when not to - suppose you are writing a representation of a generic graph, with vertices represented by objects and edges represented by pointers between the objects. The usual smart pointers will not help you: graphs can be cyclic, and no particular node can be held responsible for the memory management of other nodes, so shared and weak pointers are insufficient. You might for example put everything in a vector and use indices instead of pointers, or put everything in a deque and use raw pointers. You could use shared_ptr if you wanted, but it won't add anything except overhead. Or you could look for mark-sweep GC.

A more marginal case: I prefer to see functions take a parameter by pointer or reference, and promise not to retain a pointer or reference to it, rather than take a shared_ptr and leave you wondering whether maybe they retain a reference after they return, maybe if you modify the referand ever again you'll break something, etc. Not retaining references is something that often isn't documented explicitly, it just goes without saying. Maybe it shouldn't, but it does. Smart pointers imply something about ownership, and falsely implying that can be confusing. So if your function takes a shared_ptr, be sure to document whether it can retain a reference or not.


In many situations, I believe they are definitely the way to go (less messy cleanup code, reduced risk of leaks, etc.). However there is some very slight extra expense. If I were writing some code that had to be as fast as possible (say a tight loop that had to do some allocation and a free), I would probably not use a smart pointer in hopes of eking out a bit more speed. But I doubt that it would make any measurable difference in most situations.


In general, no you cannot use smart pointers always. For example, when you use other frameworks that don't use smart pointer (like Qt), you have to use raw pointers too.


If you are handling a resource, you should always use RAII techniques, with in the case of memory means using some form or another of a smart pointer (note: smart, not shared_ptr, choose the smart pointer that is most appropriate for your specific use case). It is the only way to avoid leaks in the presence of exceptions.

There are still cases where raw pointers are necessary, when resource management is not handled through the pointer. In particular they are the only way of having a resettable reference. Think of keeping a reference into an object whose lifetime cannot be explicitly handled (member attribute, object in the stack). But that is a very specific case that I have only seen once in real code. In most cases, using a shared_ptr is a better approach to sharing an object.


My take on smart pointers: GREAT when it's hard to know when deallocation could happen (say inside an try/catch block, or inside a function that calls a function (or even a constructor!) that could throw you out of your current function), or adding better memory management to a function that has returns everywhere in the code. Or putting pointers in containers.

Smart pointers, however, have a cost that you might not want to pay all over your program. If memory management is easy to do by hand ("Hmm, I know that when this function ends I need to delete these three pointers, and I know that this function will run to completion"), then why waste the cycles having the computer do it?


Yes BUT i have gone several projects without the use of a smart pointer or any pointers. Its good practice to use containers such as deque, list, map etc. Alternatively i use references when possible. Instead of passing in a pointer i pass a reference or const reference and its almost always illogical to delete/free a reference so i never have issue there (typically i create them on the stack by writing { Class class; func(class, ref2, ref3); }


It is. Smart pointer is one of the cornerstones of the old Cocoa (Touch) ecosystem. I believe it keeps impacting the new.

참고URL : https://stackoverflow.com/questions/2454214/is-it-a-good-practice-to-always-use-smart-pointers

반응형