Using members of base class after destruction of d

2019-07-29 22:40发布

问题:

Suppose that we have a simple struct:

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

From implementation point, the destructor RefCounters::~RefCounters should do nothing, since all its members have primitive type. This means that if an object of this type is destroyed with explicit call of destructor (but its memory is not deallocated), then we would be able to work with its members normally after the object is dead.

Now suppose that we have some more classes derived from RefCounters. Suppose that RefCounters is present exactly once among base classes of Derived class. Suppose that destructor is called explicitly for an object of class Derived, but its memory is not deallocated. Is it OK to access members strong_cnt and weak_cnt after that?

From implementation point, it should be OK, at least when there is no virtual inheritance involved. Because Derived* can be statically cast to RefCounters* (adding compile-time constant offset to address), and the memory of RefCounters should not be touched by destructor of Derived class.

Here is a code sample:

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

Is such code considered undefined behavior by C++ standard? Is there any practical reason why it can fail to work? Can it be made legal by minor changes or adding some constraints?

P.S. Supposedly, such code sample allows to make intrusive_ptr + weak_ptr combo, such that weak_ptr can be always obtained from an object pointer if at least one weak_ptr is still pointing at it. More details in this question.

回答1:

I believe that your approach is bad. There is a nice link in comments that shows debate about the details of the standard. Once there is a debate there is good chance that different compilers will implement this detail differently. Even more. The same compiler may change its implementation from one version to another.

The more you use various dark corners, the bigger is the chance that you will meet with problems.

Bottom line. What are willing to achieve? Why can't you do this using ordinary C++ language features?