开发者

How can this compile? (delete a member of const object)

I would expect an error inside the copy constructor, but this compiles just fine with MSVC10.

class Test
{
public:
    Test()
    {
        p = new int(0);
    }开发者_JS百科

    Test(const Test& t)
    {
        delete t.p; // I would expect an error here
    }

    ~Test()
    {
        delete p;
    }

private:
    int* p;
};


This is a common issue with pointers. There is no way of actually disabling code from calling delete on a pointer (other than controlling access to the destructors). The first thing that you can hear is that the delete does not modify the pointer, but rather the pointed object. This can easily be checked by printing the pointer (std::cout << static_cast<void*>(p);) before and after a delete, so even if the pointer is constant the operation is not modifying it.

A little less intuitive is the fact that you can delete a pointer to a constant element --and the delete surely modifies the pointed element. But the language needed to be able to destruct constant objects when they fell out of scope (think { const mytype var(args); }) so const-ness cannot really affect the ability to destroy an object, and if that is allowed for auto variables, it does not make much sense to change the behavior for dynamically allocated objects. So at the end this is also allowed.


The issue that you are running into here is that you are not changing p per se (thus pstays immutable as you're not changing its value), but you're changing what p points to and thus are working at one additional level of indirection. This is possible because deleteing the memory associated with a pointer doesn't change the pointer itself.

In a strict sense the const-ness of the object is preserved, even though its logical constness has been violated as you pulled the rug from underneath whatever p was pointing to.

As JonH mentioned in the comment, if you were not able to delete the object pointed to by a pointer held in a const object, you would end up with memory leaks because you wouldn't be able to clean up properly after the object.


Constants are immutable, but that doesn't guarantee that they cannot be deleted. How would you ever delete an object if delete wasn't allowed.

If you try to modify t.p that should throw an error as t is const. But deleting t is quite normal even if it is constant.


Having:

int* const p;

... does not disallow operator delete from being called on p. Having const int* also does not disallow operator delete from being called on it.

Typical operator delete implementations take void* and any pointer will be implicitly cast to it (actually this might be the standard behavior to take void* or the only reasonable way to implement one global operator delete which can delete anything). Also as an interesting tidbit, one can implement their own overloaded operator delete (either globally or per-class) which takes void* and only has to free the memory allocated by new. The destructor call is implicitly added before any call to operator delete by the compiler; operator delete does not call the dtor in its implementation.

It is also worth noting that having const Test& in this case basically modifies the member, int* p so that it's analogous to int* const p, not int const* p or const int* p.

Thus:

Test::Test(const Test& other)
{
    *other.p = 123; // this is valid
    other.p = NULL; // this is not valid
}

In other words, the pointer address is immutable, but the pointee is not. I've often encountered a lot of confusion here with respect to member function constness and the effect it has on data members which are pointers. Understanding this will give a little insight as to one of the reasons why we need the separation between iterator and const_iterator.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜