为什么我要在这种情况下使用智能指针?

for*_*818 8 c++ pointers smart-pointers

我从来没有使用任何类型的智能指针,但当主题是指针时,我几乎无处不在地阅读它们.我知道有些情况下智能指针比原始指针更好用,因为在某种程度上它们可以管理指针的所有权.但是,我仍然不知道,"我不需要智能指针"和"这是智能指针的情况"之间的界限.

可以说,我有以下情况:

class A {
public:
    double get1(){return 1;}
    double get2(){return 2;}
};
class SomeUtilityClass {
public:
    SomeUtilityClass(A* a) : a(a) {}
    double getResult(){return a->get1() + a->get2();}
    void setA(A* a){a = a;}
private:
    A* a;
};
int main(int argc, char** argv) {
    A a;
    SomeUtilityClass u(&a);
    std::cout << u.getResult() << std::endl;
    A a2;
    u.setA(&a2);
    std::cout << u.getResult() << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这当然是一个过于简单的例子.我的意思是SomeUtilityClass不应该"拥有"一个实例A(因为它只是一个实用类),因此它只是一个指针.

关于指针,我所知道的唯一可能出错的是:

  • SomeUtilityClass 可以使用空指针进行实​​例化
  • 指向的对象可能被删除/超出范围,没有SomeUtilityClass注意到它

智能指针如何帮助避免这个问题?在这种情况下,使用智能指针可以获得哪些其他好处?

PS:我知道智能指针有几个问题(例如这个).但是,如果您能告诉我有关此特定示例的影响,我将不胜感激.

Cás*_*nan 1

为了这个答案的目的,我将 setA 重新定义为:

void setA(A* new_a){a = new_a;}
Run Code Online (Sandbox Code Playgroud)

考虑:

// Using your SomeUtilityClass

int main() {
  A a;
  SomeUtilityClass u(&a);
  // We define a new scope, just because:
  {
    A b;
    u.setA(&b);
  }
  std::cout << u.getResult() << '\n';
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

作用域完成后,SomeUtilityClass有一个悬空指针并getResult()调用未定义行为。请注意,这不能通过引用来解决:您仍然会得到一个悬空的引用。

现在考虑使用智能指针的版本:

class SomeUtilityClass {
public:
    SomeUtilityClass(std::shared_ptr<A>& a) : a{a} {}
    double getResult(){return a->get1() + a->get2();}
    void setA(std::shared_ptr<A>& new_a){a = new_a;}
private:
    std::shared_ptr<A> a;
};

int main() {
  std::shared_ptr<A> a{new A};
  SomeUtilityClass u{a};
  // We define a new scope, just because:
  {
    std::shared_ptr<A> b{new A};
    u.setA(b);
  }
  std::cout << u.getResult() << '\n';
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

因为您拥有共享所有权,所以无法获得悬空指针。所指向的内存b将像往常一样被删除,但只有在u被销毁(或其指针被更改)之后。

恕我直言,在大多数情况下,您应该使用智能指针(即使一开始它似乎没有多大意义)。它使维护变得更加容易。仅在实际需要它们的特定代码中使用原始指针,并尽可能封装/隔离该代码。