C++ 将抽象 class 的派生对象添加到另一个 class 中而没有悬挂指针?
C++ Add derived object of abstract class into another class without dangling pointers?
我有一个指向抽象 class 的简单容器 class,并且我有指向容器 class 中的指针的函数 get/set。更具体地说,class 看起来像这样:
class Container
{
Abstract* thing;
public:
void set(Abstract &obj)
{
thing = &obj; //danger of dangling pointer
}
Abstract* get()
{
return thing;
}
};
Abstract
是一个摘要 class。正如已经看到的那样,存在悬空指针的危险。我知道我可以复制对象(新)然后指向它。但是我无法创建抽象的实例 class。有什么解决方案?
以下只是更多信息:
Class定义
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;
}
你需要做的是具体定义你的内存所有权。
Container::set
通过引用接受 Abstract
的实例,这通常并不意味着所有权转移:
void set(Abstract &obj){...} // Caller retains ownership of obj, but now we have a weak reference to it
那么删除的责任不在你。
Container::get
returns 一个 指针 暗示所有权,表明调用 set
的人不应该使传递的对象无效。
Abstract* get(){...}
正如您所说,这可能会有问题。
你有几个选择
- 使用适当的文档 (Code by contract)
在 Container
中编码这些内存所有权语义
- 使用像
std::shared_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)
如果您担心对象在别处被释放导致悬垂指针,您可以使用 boost 智能指针。
Boost智能指针将为您提供记账服务,帮助您避免这种情况。
一些信息可以在这里找到:
smart pointers (boost) explained
这就是 std::unique_ptr
的用途:
class Container
{
std::unique_ptr<Abstract> thing;
public:
void set(std::unique_ptr<Abstract> obj)
{
thing = obj;
}
Abstract* get()
{
return thing.get();
}
};
现在 Abstract
对象被 Container
"owned" 并在 Conatiner
被销毁时自动清理。
如果您想要一个可能寿命更长或可能在多个容器之间共享的指针,请改用 std::shared_ptr
。
我有一个指向抽象 class 的简单容器 class,并且我有指向容器 class 中的指针的函数 get/set。更具体地说,class 看起来像这样:
class Container
{
Abstract* thing;
public:
void set(Abstract &obj)
{
thing = &obj; //danger of dangling pointer
}
Abstract* get()
{
return thing;
}
};
Abstract
是一个摘要 class。正如已经看到的那样,存在悬空指针的危险。我知道我可以复制对象(新)然后指向它。但是我无法创建抽象的实例 class。有什么解决方案?
以下只是更多信息:
Class定义
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;
}
你需要做的是具体定义你的内存所有权。
Container::set
通过引用接受 Abstract
的实例,这通常并不意味着所有权转移:
void set(Abstract &obj){...} // Caller retains ownership of obj, but now we have a weak reference to it
那么删除的责任不在你。
Container::get
returns 一个 指针 暗示所有权,表明调用 set
的人不应该使传递的对象无效。
Abstract* get(){...}
正如您所说,这可能会有问题。
你有几个选择
- 使用适当的文档 (Code by contract) 在
- 使用像
std::shared_ptr
这样的智能指针
Container
中编码这些内存所有权语义
在前一种情况下,它是否有效取决于用户阅读和理解您的 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)
如果您担心对象在别处被释放导致悬垂指针,您可以使用 boost 智能指针。
Boost智能指针将为您提供记账服务,帮助您避免这种情况。
一些信息可以在这里找到: smart pointers (boost) explained
这就是 std::unique_ptr
的用途:
class Container
{
std::unique_ptr<Abstract> thing;
public:
void set(std::unique_ptr<Abstract> obj)
{
thing = obj;
}
Abstract* get()
{
return thing.get();
}
};
现在 Abstract
对象被 Container
"owned" 并在 Conatiner
被销毁时自动清理。
如果您想要一个可能寿命更长或可能在多个容器之间共享的指针,请改用 std::shared_ptr
。