【问题标题】:Smart pointer (unique_ptr) instead of raw pointer as class member智能指针(unique_ptr)而不是原始指针作为类成员
【发布时间】:2015-10-01 21:28:51
【问题描述】:

给定一个类层次结构:

class A {

private:
  string * p_str;

public:
  A() : p_str(new string())
  {
  }

  virtual ~A() {
    delete p_str;
  }
};

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

  virtual ~B() override {
  }

  virtual void Test() {
    cout << "B::Test()" << endl;
  }
};

int main(int, char**)
{
  B b;

  b.Test();

    return 0;
}

有一个p_str 指向字符串的指针(不管它指向什么对象)。

把它换成std::unique_ptr除了不写delete p_str有什么好处吗?

class A {

private:
  std::unique_ptr<string> p_str;

public:
  A() : p_str(make_unique<string>())
  virtual ~A() {}

}

?如果任何派生类的构造函数抛出异常,任何代码变体都会发生内存泄漏。

UPD我试过这个代码:

包括

#include <memory>
using namespace std;

class Pointed {
public:
  Pointed() { std::cout << "Pointed()\n"; }
  ~Pointed() { std::cout << "~Pointed()\n"; }
};

class A1 {
private:
  Pointed * p_str;

public:
  A1() : p_str(new Pointed()) {
    cout << "A1()\n";
    throw "A1 constructor";
  }

  virtual ~A1() {
    cout << "~A1()\n";
    delete p_str;
  }
};

class B1 : public A1 {
public:
  B1() {
    throw "B constructor";
  }

  virtual ~B1() override {
    cout << "~B1()\n";
  }

  virtual void Test() {
    cout << "B1::Test()" << endl;
  }
};

class A2 {
private:
  std::unique_ptr<Pointed> p_str;

public:
  A2() : p_str(make_unique<Pointed>()) {
    cout << "A2()\n";
    throw "A2 constructor";
  }
  
  virtual ~A2() {
    cout << "~A2()\n";
  }
};


class B2 : public A2 {
public:
  B2() {
    cout << "B2()\n";
    throw "B2 constructor";
  }

  virtual ~B2() override {
    cout << "~B2()\n";
  }

  virtual void Test() {
    cout << "B2::Test()" << endl;
  }
};

int main(int, char**) {
  cout << "B1::A1 (raw pointers)\n";

  try {
    B1 b1;
  }
  catch (...) {
    cout << "Exception thrown for B1\n";
  }

  cout << "\nB2::A2 (unique pointers)\n";

  try {
    B2 b2;
  }
  catch (...) {
    cout << "Exception thrown for b2\n";
  }

  cin.get();

  return 0;
}

输出是:

B1::A1 (raw pointers)
Pointed()
A1()
Exception thrown for B1

B2::A2 (unique pointers)
Pointed()
A2()
~Pointed()
Exception thrown for b2

所以,结果是unique_ptr在声明成员的同一个类的构造函数中发生异常时被自动删除。

【问题讨论】:

  • "如果任何派生类的构造函数抛出异常,任何代码变体都会发生内存泄漏。"你确定吗?在我看来就像won't be a leak regardless。由于是子对象在构造函数中抛出异常,所以抛出异常时会调用基对象的析构函数。
  • @jaggedSpire 对“任何派生”感到抱歉,但如果在定义成员的类的构造函数中发生异常,则会出现内存泄漏:coliru.stacked-crooked.com/a/3bd94aa1c8fbb7df
  • 这是正确的,但不是您最初断言的。请记住要小心措辞——当涉及到异常时,您抛出的 where 有时会产生重大影响。由于您修复了您的声明,因此它不会说如果继承类抛出异常就会发生内存泄漏,我愿意删除我原来的评论。
  • @jaggedSpire 请不要!让所有讨论留在这里,以供未来读者理解

标签: c++ pointers c++11 memory-leaks unique-ptr


【解决方案1】:

使用原始指针,您可以进行双重删除,因为您没有手动实现的复制 c-tor 和赋值运算符。

A a;
A b = a; // b.p_str store the same address, that a.p_str

使用unique_ptr,您不能复制/分配对象,但您可以移动它,而无需编写 move c-tor/move 分配运算符。

A a;
A b = a; // cannot do this, since `unique_ptr` has no copy constructor.
A b = std::move(a); // all is okay, now `b` stores `unique_ptr` with data and `a` stores `nullptr`

但实际上,我不知道为什么要在此处存储指针,而不仅仅是 std::string 类型的对象,这是您示例中的最佳解决方案。

【讨论】:

  • string 只是举例
【解决方案2】:

除了 ForEveR 的回复,使用unique_ptr 告诉读者只有一个对象 引用(在本例中,拥有)该字符串。使用裸指针,阅读您的代码的任何人都不知道有多少其他对象、本地对象或其他任何东西(单例?全局对象?)访问和/或修改该字符串。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-18
    • 1970-01-01
    • 1970-01-01
    • 2016-08-01
    • 1970-01-01
    • 2011-12-25
    • 1970-01-01
    相关资源
    最近更新 更多