我希望复制构造函数中有一个错误,但这对MSVC10编译得很好。
class Test
{
public:
Test()
{
p = new int(0);
}
Test(const Test& t)
{
delete t.p; // I would expect an error here
}
~Test()
{
delete p;
}
private:
int* p;
};
答案 0 :(得分:4)
这是指针的常见问题。实际上没有办法禁止代码在指针上调用delete
(除了控制对析构函数的访问)。您可以听到的第一件事是delete
不会修改指针,而是修改指向的对象。这可以通过在删除之前和之后打印指针(std::cout << static_cast<void*>(p);
)来轻松检查,因此即使指针是常量,操作也不会修改。
稍微不那么直观的是,您可以delete
指向常量元素的指针 - 而delete
肯定会修改指向的元素。但是当语言超出范围时,语言需要能够破坏常量对象(想象{ const mytype var(args); }
),因此常量不能真正影响破坏对象的能力,如果允许自动变量,它就会改变动态分配对象的行为没有多大意义。所以最后也允许这样做。
答案 1 :(得分:3)
您遇到的问题是,您本身并未更改p
(因此p
保持不变,因为您没有更改其值),但您正在更改{ {1}}指向并因此在一个额外的间接层面工作。这是可能的,因为删除与指针关联的内存不会改变指针本身。
严格意义上说,即使在从p
指向的任何p
下面拉出地毯时,也会保留其逻辑常量,从而保留对象的常量。
正如JonH在评论中提到的那样,如果你无法删除const对象中保存的指针所指向的对象,那么最终会导致内存泄漏,因为你无法在对象
答案 2 :(得分:2)
常量是不可变的,但这并不能保证它们不能被删除。如果不允许删除,你将如何删除对象。
如果你试图修改应该抛出错误的t.p,因为t是const。但删除t是很正常的,即使它是恒定的。
答案 3 :(得分:0)
有:
int* const p;
...不禁止在 p 上调用operator delete。 const int*
也不允许在其上调用operator delete。
典型的运算符删除实现采用void *并且任何指针都将被隐式地转换为它(实际上这可能是采用void *的标准行为,或者是实现一个可以删除任何内容的全局运算符delete的唯一合理方法)。另外,作为一个有趣的小工具,可以实现自己的重载操作符删除(全局或每个类),它取出void *并且只需释放new分配的内存。在编译器调用operator delete之前隐式添加析构函数调用; operator delete不会在其实现中调用dtor。
值得注意的是,在这种情况下const Test&
基本上会修改成员int* p
,使其类似于int* const p
,而不是int const* p
或{{1} }。
因此:
const int* p
换句话说,指针地址是不可变的,但指针不是。关于成员函数constness及其对作为指针的数据成员的影响,我经常遇到很多困惑。理解这一点将对我们需要在iterator和const_iterator之间进行分离的原因之一提供一些见解。