通过shared_ptr共享类内存

时间:2015-03-26 00:27:45

标签: c++ inheritance shared-ptr

我想在不同对象之间共享对象内存(例如,Reader / Writer访问相同的内存池)。它工作状态良好,但我遇到共享一个shared_ptr的问题。

struct A {
  A() {}

  A(const A &other) {
    i = other.i;
  }

  std::shared_ptr<int> i;
};

struct B : public A {
  B(const A &other) : A(other) {}
};

我想让第二个例子起作用,但它会引发异常。因为变量i没有被初始化并且shared_ptr没有被复制(他是空的)。

{ // don´t throw
    A a;
    a.i = std::make_shared<int>(10);

    B b(a);
    *b.i = 11;

    printf("%d\n", *a.i);
}  
{ // throw
    A a;
    B b(a);

    b.i = std::make_shared<int>(10);
    printf("%d\n", *a.i);
}

只有B应该是init变量i。

下面会有一个解决方案,但是我真的需要一个其他的包装类吗?

struct A {
  A() : i(std::make_shared<std::vector<std::shared_ptr<int>>>()) {}

  A(const A &other) {
    i = other.i;
  }

  std::shared_ptr<std::vector<std::shared_ptr<int>>> i;
};

struct B : public A {
  B(const A &other) : A(other) {}
};

int main(int argc, char *argv[]) {

  { // throw
    A a;
    B b(a);

    b.i->emplace_back(std::make_shared<int>(10));
    printf("%d\n", *a.i->at(0));
  }
}

另一个例子就是使用原始指针,但我想问你,它如何与shared_ptr一起使用。

int类型只是一个例子。它也可能是一个没有默认构造函数的繁重类。

1 个答案:

答案 0 :(得分:1)

您的第一个场景

您的代码会抛出,因为:

  • 当您创建a时,a.i为空shared_ptr
  • 然后使用复制b的构造函数创建a。因此,b.i为空shared_ptr
  • 然后您将新创建的对象的共享指针分配给b.i。但这并没有改变a.i指针,它仍然是空的。
  • 最后你尝试取消引用a.i。但由于a.i为空,即使用次数为0且没有有效指针,因此它的未定义行为(可能会发生段错误)。

第一种情况的改进:

通过定义A:

的默认构造函数,您可以轻松避免这个陷阱
    A() : i(std::make_shared<int>(0)) {}
然后

ab将指向同一个共享对象,您将不会遇到段错误。

但是这种方法当然不会阻止某人将b.i重新分配给另一个共享指针。那是struct的问题:你把钥匙交给房子,由你来清理这个烂摊子。

改进的变体可以是完全封装的类,其中我将受到保护,函数或操作符可以访问i。我选择了一种方法,我重写赋值形式int和转换为int,以允许直观的用法,但这是一个品味的问题:

class A {
public:
    A() : i(std::make_shared<int>(0)) {}
    A(const A &other) { i = other.i; }
    operator int() { return *i; }   // you can use A when you could use an int
    A& operator= (int x) {
        *i = x;
        return *this;
    }
    // todo:  copy assigment:  take the pointer or take the value ? 
protected:
    std::shared_ptr<int> i;
};
struct B : public A {
    B(const A &other) : A(other) {}
    B& operator= (int x) {
        *i = x;
        return *this;
    }
    // todo:  copy assigment:  take the pointer or take the value ? 
}; 

这个类的用法是:

{ // don´t throw
    A a;
    a = 10;
    B b(a);
    b = 11;
    printf("%d\n", (int)a);
}
{ // don't throw either
    A a;
    B b(a);
    a = 1; 
    cout << a <<endl; 
    cout << b << endl;
    b = 10; 
    printf("%d\n", (int)a);  // to make sure that only the int value is passed
}   

您的第二个场景

在这种情况下,您已更改为使用共享指针向量的共享指针。

我发现此代码没有任何问题,而且我没有投掷过:see online demo

您的其他想法

您可以使用原始指针,前提是它们已经正确分配了新的指针。

int *pi = new int(1); 
shared_ptr<int> spi(pi); 

但注意:一旦你这样做,shared_ptr就拥有了所有权。这意味着shared_ptr负责销毁对象。

如果你要在另一个shared_ptr中重用这个原始指针(或者更糟糕的是:如果它是从shared_ptr获得的),你的编译器会抱怨,但你在运行时会得到未定义的行为,因为当第二个shared_ptr将尝试销毁已被第一个shared_ptr销毁的对象(如果从原始指针构造,shared_ptr不会意识到其他shared_ptr的存在)。