C++ C++;在没有悬空指针的情况下将抽象类的派生对象添加到另一个类中?

C++ C++;在没有悬空指针的情况下将抽象类的派生对象添加到另一个类中?,c++,containers,abstract-class,dangling-pointer,C++,Containers,Abstract Class,Dangling Pointer,我有一个简单的容器类,它指向一个抽象类,我有一些函数来获取/设置容器类中的指针。更具体地说,该类如下所示: class Container { Abstract* thing; public: void set(Abstract &obj) { thing = &obj; //danger of dangling pointer } Abstract* get() { return thing;

我有一个简单的容器类,它指向一个抽象类,我有一些函数来获取/设置容器类中的指针。更具体地说,该类如下所示:

class Container
{
    Abstract* thing;
public:
    void set(Abstract &obj)
    {
        thing = &obj; //danger of dangling pointer
    }

    Abstract* get()
    {
        return thing;
    }
};
Abstract
是一个抽象类。正如我们已经看到的,存在指针悬空的危险。我知道我可以复制一个对象(新的),然后指向它。但是我不能创建抽象类的实例。有什么解决办法


以下只是更多信息:

类别定义

class Abstract
{
public:
    virtual void something() = 0;
};

class Base : public Abstract
{
    int a;
public:
    Base() {}
    Base(int a) : a(a){}
    virtual void something()
    {
        cout << "Base" << endl;
    }
};

class Derived : public Base
{
    int b;
public:
    Derived() {}
    Derived(int a, int b) : Base(a), b(b){}
    virtual void something()
    {
        cout << "Derived" << endl;
    }
};
void setBase(Container &toSet)
{
    Base base(15);
    toSet.set(base);
}

void setDerived(Container &toSet)
{
    Derived derived(10, 30);
    toSet.set(derived);
}

int main()
{
    Container co;

    Base base(15);
    Derived derived(10, 30);

    Base *basePtr;
    Derived *derivedPtr;

    //This is fine
    co.set(base);
    basePtr = static_cast<Base *>(co.get());
    basePtr->something();

    //This is fine
    co.set(derived);
    derivedPtr = static_cast<Derived *>(co.get());
    derivedPtr->something();

    //Reset
    basePtr = nullptr;
    derivedPtr = nullptr;

    //Dangling pointer!
    setBase(co);
    basePtr = static_cast<Base *>(co.get());
    basePtr->something();

    //Dangling pointer!
    setDerived(co);
    derivedPtr = static_cast<Derived *>(co.get());
    derivedPtr->something();

    return 0;
}
类摘要
{
公众:
虚空某物()=0;
};
类库:公共摘要
{
INTA;
公众:
Base(){}
基(inta):a(a){}
虚拟空某物
{
不能做某事;
返回0;
}

您需要做的是具体定义内存所有权

Container::set
通过引用接受
Abstract
的实例,这通常并不意味着所有权转移:

 void set(Abstract &obj){...} // Caller retains ownership of obj, but now we have a weak reference to it
那么删除的责任就不在你身上了

Container::get
返回一个表示所有权的指针,指示调用
set
的人不应使传递的对象无效

Abstract* get(){...}
正如你所说的,这可能会有问题

你有几个选择

  • 使用适当的文档在
    容器中对这些内存所有权语义进行编码()
  • 使用智能指针,如
    std::shared\u ptr
在前一种情况下,它是否起作用取决于用户阅读和理解您的API,然后与之配合良好。在后一种情况下,指针对象拥有自己,并在最后一个实例超出范围时删除分配的内存

 void set(std::shared_ptr<Abstract> obj){...} 
// now Container participates in the lifetime of obj, 
// and it's harder to nullify the underlying object 
// (you'd have to be intentionally misbehaving)
void集(std::shared_ptr obj){…}
//现在容器参与了obj的生命周期,
//而且很难使底层对象无效
//(你必须故意行为不端)

如果您担心对象被释放到其他地方会导致指针悬空,可以使用boost智能指针

Boost smart pointers将为您提供簿记服务,并帮助您避免此类情况

可在此处找到一些信息:
这就是
std::unique\u ptr
的作用:

class Container
{
    std::unique_ptr<Abstract> thing;
public:
    void set(std::unique_ptr<Abstract> obj)
    {
        thing = obj;
    }

    Abstract* get()
    {
        return thing.get();
    }
};
类容器
{
std:独一无二的东西;
公众:
无效集(标准::唯一对象)
{
事物=对象;
}
抽象*get()
{
return thing.get();
}
};
现在,
Abstract
对象由
Container
拥有,当
Container
被销毁时,该对象将被自动清除


如果您想要一个寿命更长的指针,或者可以在多个容器之间共享,请改用
std::shared_ptr

是否愿意共享下一票的原因?当您用鼠标指针悬停DV按钮时,工具提示中可能会共享@silentwf下一票的原因。因为我讨厌像这样的愚蠢行为
delete myContainer.get();
我能和你谈谈
Abstract&get(){…}
?@user4581301:你为什么要删除
myContainer.get()
?它会自动删除,手动调用
delete
会导致分段错误。是的。这很粗心,但你可以做到。问题可能不是明显的即时错误,而是在几十行代码之后错误处理了看起来像原始指针的内容。该参考增加了分段的难度重新回到故意行为不端的类别。@AndyG好奇上半部分是如何解决问题的?假设没有“得到”函数,则我仍然必须使用智能指针否?如果用户表现良好,则不需要使用智能指针。契约式设计假定满足先决条件和后决条件。因此,
get
的一个先决条件是,在先前的
集合中添加的数据未失效。