2016-12-25 72 views
1

假设我们有一个简单的结构:派生类的破坏后基类的使用成员

struct RefCounters { 
    size_t strong_cnt; 
    size_t weak_cnt; 
    RefCounters() : strong_cnt(0), weak_cnt(0) {} 
}; 

从实现的角度,析构函数RefCounters::~RefCounters应该什么都不做,因为它的所有成员都有基本类型。这意味着如果这个类型的对象被析构函数的显式调用破坏了(但是它的内存是而不是解除分配),那么在对象死后,我们将能够正常地使用它的成员。

现在假设我们有一些从RefCounters派生的类。假设RefCounters仅在Derived类的基类中存在一次。假设为类Derived的对象显式调用析构函数,但其​​内存为而不是解除分配。之后可以访问会员strong_cntweak_cnt吗?

从实现的角度来看,应该没问题,至少在没有涉及虚拟继承的情况下。因为Derived*可以静态转换为RefCounters*(将编译时常量偏移量加到地址中),并且RefCounters的存储器不应该被Derived类的析构函数触及。

下面是一个代码示例:

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"; 

被认为是不确定的行为,例如由代码C++标准?有没有实际的原因可能导致它失效?可以通过微小的更改或添加一些约束来使其合法化吗?

P.S.假设这样的代码示例允许创建intrusive_ptr + weak_ptr组合,这样如果至少有一个weak_ptr指向它,总是可以从对象指针获取weak_ptr。更多详情,请参阅this question

+0

在一个不相关的说明中,为什么'RefCounters'本身不处理它的计数器呢?意思是析构函数*会做些什么(即减少一个或两个计数器)? –

+1

由于'RefCounters'具有一个微不足道的析构函数,因此每当** [basic.life]/1 **时其存储被重用或释放时,它的生存期结束。显式的析构函数调用是不可操作的,不应该影响任何东西; “RefCounters”的特定实例也不是大对象的子对象。 –

+0

@IgorTandetnik:谢谢你的评论。我想它应该使合法的第一种情况(即没有派生类)。但我仍然不确定派生类的第二种情况。 – stgatilov

回答

0

我相信你的方法不好。评论中有一个很好的链接,显示了关于标准细节的争论。一旦有争论,不同的编译器就会有不同的实现细节的机会。更。同一编译器可能会将其实现从一个版本更改为另一个版本。

你使用各种黑暗角落的次数越多,遇到问题的机会就越大。

底线。什么愿意实现?为什么你不能使用普通的C++语言功能来做到这一点?

+0

只需阅读“P.S.”问题的一部分,我在那里添加了一个链接。但请不要发布“你不应该这样做”的东西,因为这不是一个好的答案。 – stgatilov

+0

@stgatilov,看起来你需要重新修改你的问题,因为问题“是基类(和它的**原始**数据成员)在销毁派生类后仍然存在”与讨论设计智能指针的方式非常无关。 –

+0

你问“这段代码是否合法?”,“它可以被修改为合法吗?”我的意思是,既然即使在委员会中也存在这样的争论,所以应该把疑虑转移到“非法”的方向。代码是非法的,至少在辩论得到解决之前,编译器将与此决议保持一致。 –

相关问题