C++ C++;如何知道某个对象何时被销毁
我不明白当一个死对象是从另一个作用域创建的,并且被放在另一个作用域中的某种容器中时,我如何阻止访问它。范例C++ C++;如何知道某个对象何时被销毁,c++,c++98,C++,C++98,我不明白当一个死对象是从另一个作用域创建的,并且被放在另一个作用域中的某种容器中时,我如何阻止访问它。范例 #include <iostream> #include <string> #include <vector> class Foo { public: void Speak(){ std::cout << "ruff"; } }; int main
#include <iostream>
#include <string>
#include <vector>
class Foo {
public:
void Speak(){
std::cout << "ruff";
}
};
int main()
{
std::vector<Foo*> foos;
{
Foo foo;
foos.push_back(&foo);
// foo is now dead
}
for(size_t i = 0; i < foos.size(); i++){
// uh oh
foos[i]->Speak();
}
}
如果我们有一个名为MyObject
的组件,其中包含一个我们想要监听的EventEmitter
,情况会变得更糟。如果MyObject
被复制,现在我们有了这个内部EventEmitter
,其中的处理程序引用了可能不再存在的MyObject
所以我可能会把它全部扔掉,转而使用回调。。但即使如此,某些对象仍然拥有可能不再存在的PTR或对某些其他对象的引用!我们可以尽可能小心,但我们永远不知道会发生什么
我想我需要说的是
…我应该坚持平面设计。你为什么不使用
std::vector
而不是std::vector
?在前一种方式中,我认为没有办法阻止Foo foo;
foo将在超出范围时销毁。
所以,不应该使用局部变量。
使用动态对象(例如):
它会起作用的。
你要负责销毁对象。如果你想让对象的生存期超出创建对象的范围,那么必须使用动态生存期来创建该对象。您可以使用
new
执行此操作:
std::vector<Foo*> foos;
{
Foo* foo = new Foo;
foos.push_back(foo);
}
理想情况下,您可以使用某种类型的智能指针来管理动态生命周期对象,但C++98中没有标准的智能指针
std::unique_ptr
和std::shared_ptr
std::auto_ptr
可用,但该类很容易被错误使用。为您编写一个简单的共享类可能是值得的。如果您不需要支持弱指针和原子操作,那么它就不会太复杂。下面是一个非常基本的实现:
template <typename T>
class shared_ptr
{
private:
struct control_block
{
control_block(T* ptr)
: ref_count_(1),
ptr_(ptr)
{}
~control_block()
{
delete ptr_;
}
size_t ref_count_;
T* ptr_;
};
control_block* control_block_;
public:
shared_ptr()
: control_block_(NULL)
{}
shared_ptr(T* ptr)
: control_block_(new control_block(ptr))
{}
shared_ptr(const shared_ptr& other)
: control_block_(other.control_block_)
{
++control_block_->ref_count_;
}
shared_ptr& operator=(shared_ptr other)
{
std::swap(control_block_, other.control_block_);
return *this;
}
~shared_ptr()
{
if (control_block_) {
--control_block_->ref_count_;
if (control_block_->ref_count_ == 0) {
delete control_block_;
}
}
}
T& operator*() { return *control_block_->ptr_; }
T* operator->() { return control_block_->ptr_; }
bool operator==(const shared_ptr& other)
{
return control_block_ == other.control_block_;
}
};
模板
类共享\u ptr
{
私人:
结构控制块
{
控制块(T*ptr)
:参考计数(1),
ptr(ptr)
{}
~control_block()
{
删除ptr;
}
尺寸参考计数;
T*ptr;
};
控制块*控制块;
公众:
共享_ptr()
:控制块(空)
{}
共享ptr(T*ptr)
:控制块(新控制块(ptr))
{}
共享\u ptr(常数共享\u ptr和其他)
:控制块(其他控制块)
{
++控制块->参考计数;
}
共享\u ptr&运算符=(共享\u ptr其他)
{
标准::交换(控制块、其他控制块);
归还*这个;
}
~shared_ptr()
{
如果(控制块){
--控制块->参考计数;
如果(控制块->参考计数=0){
删除控制块;
}
}
}
T&运算符*(){return*控制块->ptr}
T*运算符->(){返回控制块->ptr;}
布尔运算符==(常数共享\u ptr和其他)
{
返回控件\u块==其他控件\u块;
}
};
基本前提是给定对象的所有shared_ptr
s都持有指向相同控制块的指针。每当复制共享ptr
时,其控制块
的参考计数将增加;每当共享ptr
被销毁时,其控制块
的参考计数将减少。如果引用计数曾经达到零,它将删除控制块以及指向的对象。您可以使用将向量赋给foo本身并删除析构函数中的指针。基本上,对象本身负责记录指针
#include <iostream>
#include <string>
#include <vector>
class Foo {
std::vector <Foo *> *v;
public:
Foo(std::vector <Foo *> *v): v(v){}
void Speak(){
std::cout << "ruff";
}
~Foo() {
auto it = std::find(v->begin(), v->end(), this);
if (it != v->end())
{
v->erase(it);
}
}
};
int main()
{
std::vector<Foo*> foos;
{
Foo foo(&foos);
foos.push_back(&foo);
// foo is now dead
}
std::cout << "length is " << foos.size() << '\n';
for(size_t i = 0; i < foos.size(); i++){
// uh oh
foos[i]->Speak();
}
}
我想把我自己的答案放在这里,展示我在不知不觉中想要写的东西:垃圾收集。更重要的是,在已经自动管理的内存上
这花了大约3天的时间才理解。我正在创建一个节点和引用系统,以使对象保持活动状态。垃圾收集
Woopsies.但这不一样,因为对象会被复制到向量中Foo
可能没有复制构造函数,或者复制它可能会产生不必要的副作用。(是的,这段代码中没有,但这只是示例代码…)此外,在推送后修改foo
,也不会在向量中更改它的新实例。对于std::move
它可以这样工作,但这不是C++98。如果Foo
是一个非常复杂的对象,该怎么办?如果复制它的行为有如此多的副作用,我必须保持。我说像vector这样的容器应该是1,对吗。只有在堆(新)2上创建对象时才有PTR。否则,它将对其对象进行100%的管理。@ony_pox232——您关心的问题实际上与vector
无关。一个简单的Foo数组
会引起与您提到的相同的问题。它不太复杂
-好的,我觉得我几乎可以写任何东西。但是有什么地方我可以得到共享PTR的概要吗?他们使用什么机制?@ony_pox232添加了一个基本示例,并解释了前提std::shared_ptr
有一些额外的特性和优化,但它使用相同的基本前提
for (std::size_t i = 0; i < foos.size(); ++i) {
delete foos[i];
}
template <typename T>
class shared_ptr
{
private:
struct control_block
{
control_block(T* ptr)
: ref_count_(1),
ptr_(ptr)
{}
~control_block()
{
delete ptr_;
}
size_t ref_count_;
T* ptr_;
};
control_block* control_block_;
public:
shared_ptr()
: control_block_(NULL)
{}
shared_ptr(T* ptr)
: control_block_(new control_block(ptr))
{}
shared_ptr(const shared_ptr& other)
: control_block_(other.control_block_)
{
++control_block_->ref_count_;
}
shared_ptr& operator=(shared_ptr other)
{
std::swap(control_block_, other.control_block_);
return *this;
}
~shared_ptr()
{
if (control_block_) {
--control_block_->ref_count_;
if (control_block_->ref_count_ == 0) {
delete control_block_;
}
}
}
T& operator*() { return *control_block_->ptr_; }
T* operator->() { return control_block_->ptr_; }
bool operator==(const shared_ptr& other)
{
return control_block_ == other.control_block_;
}
};
#include <iostream>
#include <string>
#include <vector>
class Foo {
std::vector <Foo *> *v;
public:
Foo(std::vector <Foo *> *v): v(v){}
void Speak(){
std::cout << "ruff";
}
~Foo() {
auto it = std::find(v->begin(), v->end(), this);
if (it != v->end())
{
v->erase(it);
}
}
};
int main()
{
std::vector<Foo*> foos;
{
Foo foo(&foos);
foos.push_back(&foo);
// foo is now dead
}
std::cout << "length is " << foos.size() << '\n';
for(size_t i = 0; i < foos.size(); i++){
// uh oh
foos[i]->Speak();
}
}
➜ test ./a.out
length is 0