C++ 在销毁派生类后使用基类的成员
假设我们有一个简单的结构:C++ 在销毁派生类后使用基类的成员,c++,language-lawyer,destructor,C++,Language Lawyer,Destructor,假设我们有一个简单的结构: struct RefCounters { size_t strong_cnt; size_t weak_cnt; RefCounters() : strong_cnt(0), weak_cnt(0) {} }; 从实现的角度来看,析构函数RefCounters::~RefCounters应该什么都不做,因为它的所有成员都具有基元类型。这意味着,如果使用析构函数的显式调用销毁了此类型的对象(但其内存未释放),那么在对象死亡后,我们将能够正常处理
struct RefCounters {
size_t strong_cnt;
size_t weak_cnt;
RefCounters() : strong_cnt(0), weak_cnt(0) {}
};
从实现的角度来看,析构函数RefCounters::~RefCounters
应该什么都不做,因为它的所有成员都具有基元类型。这意味着,如果使用析构函数的显式调用销毁了此类型的对象(但其内存未释放),那么在对象死亡后,我们将能够正常处理其成员
现在假设我们还有一些从RefCounters
派生的类。假设RefCounters
在派生的
类的基类中正好存在一次。假设为类派生的对象显式调用析构函数,但其内存未释放。之后可以访问成员强\u cnt
和弱\u cnt
从实现的角度来看,它应该是可以的,至少在不涉及虚拟继承的情况下是可以的。因为派生的*
可以静态地强制转换到引用计数器*
(将编译时常量偏移量添加到地址),并且派生的
类的析构函数不应触及引用计数器
的内存
下面是一个代码示例:
struct RefCounted : public RefCounters {
virtual ~RefCounted() {}
};
struct Base : public RefCounted {
int val1;
virtual void print();
};
struct Derived : public Base {
std::string val2;
virtual void print();
};
Derived *pDer = new Derived();
pDer->~Derived(); //destroy object
pDer->strong_cnt++; //modify its member
std::cout << pDer->strong_cnt << pDer->weak_cnt << "\n";
struct RefCounted:公共RefCounters{
虚拟~RefCounted(){}
};
结构基:公共引用计数{
int val1;
虚拟空打印();
};
结构派生:公共基{
std::字符串val2;
虚拟空打印();
};
派生*pDer=新派生();
pDer->~派生()//摧毁目标
pDer->strong_cnt++//修改其成员
我认为你的方法不好。评论中有一个很好的链接,显示了关于标准细节的争论。一旦出现争论,不同的编译器很有可能以不同的方式实现这个细节。甚至更多。同一个编译器可能会将其实现从一个版本更改为另一个版本
你越多地使用各种暗角,你遇到问题的机会就越大
底线。我们愿意实现什么?为什么你不能用普通的C++语言来做这个? 我相信你的方法很差。评论中有一个很好的链接,显示了关于标准细节的争论。一旦出现争论,不同的编译器很有可能以不同的方式实现这个细节。甚至更多。同一个编译器可能会将其实现从一个版本更改为另一个版本
你越多地使用各种暗角,你遇到问题的机会就越大
底线。我们愿意实现什么?为什么不能使用普通C++语言来实现这一点?因为<代码> Rebug St/Cuth>有一个平凡的析构函数,它的生命周期结束时,它的存储被重用或释放,每[Basic .Leave]/1结束。显式析构函数调用是无操作,不应影响任何内容;RefCounters
的特定实例是更大对象的子对象这一事实也不应该存在。因此,RefCounters
对象在pDer->~Derived()之后应该仍然是活动的代码>-但我相信通过pDer
访问它会表现出未定义的行为。不过,类似的方法应该可以工作:RefCounters*pRef=pDer;pDer->~派生();pRef->strong_cnt++代码>另见:@stgatilovstatic_cast(pDer)
被[basic.life]/(5.4)明确禁止:“在对象的生存期开始之前,但在分配对象将占用的存储之后,或者在对象的生存期结束之后,在重用或释放对象占用的存储之前,任何指向对象将位于或曾经位于的存储位置的指针都可以使用,但只能以有限的方式使用。。。程序具有未定义的行为,如果:。。。指针被用作static\u cast
..“@stgatilov但是,RefCounters*pRef=pDer;
应该是有效的,我认为。[basic.life]/(5.3)说:“…程序有未定义的行为,如果:。。。指针被隐式转换(4.10)为指向虚拟基类的指针。。。这似乎表明隐式转换为指向非虚拟基类的指针是可以的。事实上,据我所知,virtual
一词是通过决议添加到本条款中的,目的是为了允许这种隐式转换。由于RefCounters
有一个微不足道的析构函数,因此根据[basic.life]/1,当其存储被重用或释放时,其生存期结束。显式析构函数调用是无操作,不应影响任何内容;RefCounters
的特定实例是更大对象的子对象这一事实也不应该存在。因此,RefCounters
对象在pDer->~Derived()之后应该仍然是活动的代码>-但我相信通过pDer
访问它会表现出未定义的行为。不过,类似的方法应该可以工作:RefCounters*pRef=pDer;pDer->~派生();pRef->strong_cnt++代码>另见:@stgatilovstatic_cast(pDer)
被[basic.life]/(5.4)明确禁止:“在对象的生存期开始之前,但在分配对象将占用的存储之后,或者在对象的生存期结束之后,在重用或释放对象占用的存储之前,任何指向对象将位于或曾经位于的存储位置的指针都可以使用,但只能以有限的方式使用。。。程序具有未定义的行为,如果:。。。指针被用作static\u cast
..“@stgatilov但是,RefCounters*pRef=pDer;
应该是有效的,我认为。[basic.life]/(5.3)说:“…程序有未定义的行为,如果:。。。指针被隐式转换(4.10)为指向虚拟基类的指针……”,这似乎表明隐式转换为指向非虚拟基类的指针是可以的