简体   繁体   English

通过shared_ptr共享类内存

[英]Share class memory over shared_ptr

I want to share object memory between different objects (eg Reader/Writer access same memory pool). 我想在不同对象之间共享对象内存(例如,读取器/写入器访问相同的内存池)。 It works quit good but I have a problem with sharing one shared_ptr. 它工作正常,但是共享一个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 would like to get the second example to work, but it throws an exception. 我想让第二个示例工作,但是会引发异常。 Because the variable i is not initialized and the shared_ptr does not get copied (he is empty). 因为变量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);
}

Only B should init variable i. 只有B应该初始化变量i。

One solution would be following, but do I really need an other wrapper class? 接下来是一个解决方案,但是我真的需要另一个包装器类吗?

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));
  }
}

Another example would be just to use raw pointer but I want to ask you, how it could work with a shared_ptr. 另一个例子是仅使用原始指针,但我想问你,它如何与shared_ptr一起工作。

The type int is just an example. int类型只是一个例子。 It could also be an heavy class without default constructor. 如果没有默认构造函数,它也可能是一个繁重的类。

Your first scenario 您的第一种情况

Your code throws, because: 您的代码引发,因为:

  • when you create a , ai is an empty shared_ptr 当你创建aai是一个空shared_ptr
  • then you create b with a constructor copying a . 然后使用复制a的构造函数创建b So bi is then an empty shared_ptr 所以bi是一个空的shared_ptr
  • then you assign a shared pointer to a newly created object to bi . 然后将共享指针分配给bi的新创建对象。 But this doesn't change the ai pointer, which is still empty. 但这不会更改ai指针,后者仍然为空。
  • finally you try to dereference ai . 最后,您尝试取消引用ai But as ai is empty, ie has a usage count of 0 and no valid pointer, it's undefined behaviour (probably a segfault will happen). 但是由于ai为空,即使用计数为0并且没有有效的指针,因此它是未定义的行为(可能会发生段错误)。

Improvement of first scenario: 第一种情况的改进:

You could easily avoid this pitfall by defining the default constructor of A: 通过定义A的默认构造函数,您可以轻松避免这种陷阱:

    A() : i(std::make_shared<int>(0)) {}

a and b would then point to the same shared object, and you wouldn't experience the segfault. 然后, ab将指向相同的共享对象,并且您将不会遇到段错误。

But this approach doesn't of course prevent that someone reassigns bi to another shared pointer. 但是,这种方法当然不能防止有人将bi重新分配给另一个共享指针。 That's the problem with the struct : you give the key to the house and it's up to you to clean the mess. 这就是struct的问题:您将钥匙交给房子,然后由您来清理混乱。

An improved variant could be a completely encapsulated class, where i would be protected and functions or operator give access to i. 改进的变体可以是完全封装的类,其中我将受到保护,而函数或运算符可以访问i。 I choose a way where I overload assignment form int and converion to int, to allow an intuitive usage, but this is a matter of taste: 我选择了一种方法,可以将赋值形式int和converion重载为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 ? 
}; 

The usage of this class would then be: 此类的用法为:

{ // 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
}   

Your second scenario 您的第二种情况

In this scenario, you've changed to use a shared pointer to a vector of shared pointer. 在这种情况下,您已更改为使用共享指针指向共享指针的向量。

I can see no problem with this code and I've experienced no throwing: see online demo 我看不到这段代码有问题,也没有抛出异常: 请参阅在线演示

Your other thoughts 你的其他想法

You can of course work with raw pointers, provided they've been properly allocated with new. 如果原始指针已正确分配了新指针,那么您当然可以使用它们。

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

But attention: once you do this, the shared_ptr has ownership. 但请注意:执行此操作后,shared_ptr便拥有所有权。 THis means that the shared_ptr becomes responsible for the destruction of the object. 这意味着shared_ptr负责破坏对象。

If you would reuse this raw pointer in another shared_ptr (or worse: if it was obtained from a shared_ptr), your compiler would'nt complain, but you'd get undefined behaviour at runtime, because when the the second shared_ptr would try to destroy an object that was already destroyed by the first shared_ptr (shared_ptr won't be aware of existance of other shared_ptr if constructed from a raw pointer). 如果要在另一个shared_ptr中重用此原始指针(或更糟糕的是,如果它是从shared_ptr获得的),则编译器不会抱怨,但是在运行时会出现未定义的行为,因为当第二个shared_ptr试图破坏时一个已经被第一个shared_ptr销毁的对象(如果从原始指针构造,则sharedd_ptr将不知道其他shared_ptr的存在)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM