#include<iostream>
using namespace std;
template<class T>
class autoPtr
{
public:
autoPtr(T* ptr)
{
cout<<"autoPtr ctr"<<endl;
loc=ptr;
}
autoPtr()
{
loc=NULL;
cout<<"autoPtr dflt ctr"<<endl;
}
~autoPtr()
{
cout<<"autoPtr dtr"<<endl;
delete loc;
}
//assignment operator
autoPtr& operator=(autoPtr& rRef)
{
cout<<"autoPtr assignment operator"<<endl;
loc=rRef.loc;
rRef.loc=NULL;
return *this;
}
T* operator->()
{
cout<<"address -"<<loc<<endl;
return loc;
}
private:
T* loc;
};
class base
{
public:
base()
{
cout<<"base ctr"<<endl;
}
~base()
{
cout<<"base dtr"<<endl;
}
void printHello(int i)
{
cout<<"HELLO : "<<i<<endl;
}
};
int main()
{
autoPtr<base> ptr(new base());
autoPtr<base> ptr1;
ptr1=ptr;
ptr1->printHello(1);
ptr->printHello(2); //should make the program terminate, but not so ?
}
问题是:
ptr->printHello(2);
应该使程序终止,但事实并非如此。为什么不?
答案 0 :(得分:8)
因为你很幸运。您的程序会导致未定义的行为。
ptr1 = ptr
此代码为第一个auto_ptr
对象ptr
分配NULL地址,为第二个对象ptr1
分配一些非NULL地址,源对象在赋值期间丢失引用(=) 。
执行声明时:
ptr->printHello(2);
ptr
是NULL
指针,取消引用NULL指针是未定义行为。
但是因为在函数printHello()
内你没有访问任何类成员变量,所以它工作正常。向您的班级添加成员变量,然后尝试在printHello()
函数中访问该变量,您会看到它(很可能)崩溃
重要的是要注意未定义的行为意味着可能发生任何事情,并且无法根据C ++标准中的语言规范来定义行为。在这种情况下,它起作用的事实并不能保证它始终存在并且它仍然是未定义的行为。
答案 1 :(得分:4)
ptr->printHello(2);//should make the program terminate.but not so...y ??
不一定。它实际上是未定义的行为。如果你幸运的话,你的程序会崩溃。
未定义的行为不保证任何定义的行为。因此,您不知道执行上述行时可能会发生什么。
答案 2 :(得分:3)
你犯了一个 BIG 错误,越早纠正它,你的C ++生活就会越好。这个错误让崩溃与错误混淆。
取消引用NULL指针是“未定义的行为”。这并不意味着您将收到运行时错误。这并不意味着你的程序会崩溃。这并不意味着任何有用的事情都会发生。这并不意味着你可能会发生任何事情。
这意味着任何事情都可能发生。
不包括任何内容。
实际上“没有”是一个非常常见的案例,也是一个非常危险的案例。即使存在未定义的行为错误,程序仍然可以正常工作。
直到当然是大型演示日,当他们在你的脸上崩溃时,你的表演会让你的表演变得悲惨,而且只是为了嘲笑它。
你会开始责怪操作系统,编译器,硬件,“运气不好”等等。
未定义的行为,加上C ++复杂性以及有时不合逻辑的规则和选择,使得语言非常危险,无法通过实验学习。 仔细思考你编写的每个C ++行。无法言喻的语言是你永远不会犯这样的错误。
C ++中没有“运行时错误天使”。只是“未定义的行为守护进程”。
PS:您的实现不处理复制构造函数。
答案 3 :(得分:1)
每个人都指出使用NULL指针是Undefined Behavior而不是保证崩溃当然是100%正确。
然而,在这种情况下,有一些非常具体的东西使得不太可能,这里的结果将是崩溃:那就是你调用的printhello()
方法不是虚拟功能。这意味着进行调用的代码实际上并不需要知道对象地址:它是(或者至少可以,并且我相信通常是)链接的&amp;调用的方式与简单的全局函数几乎相同。
当然会计算对象地址并将其作为隐藏this
参数传递给函数(因为它不是静态函数),但这并不意味着无法调用该函数。我想如果你在this
中打印出printhello()
,你会看到它打印为0,但这并不意味着你的程序会崩溃。
当然,正如其他人所指出的那样,只要您引用任何成员变量,或者调用任何虚拟方法,就会非常崩溃(因为在这种情况下,您正在使用{{ 1}}作为指向对象的指针,它不再是(一个重要的演示也可能增加崩溃的可能性!)。