这是我的c ++代码:
class Sample
{
public:
int *ptr;
Sample(int i)
{
ptr = new int(i);
}
~Sample()
{
delete ptr;
}
void PrintVal()
{
cout << "The value is " << *ptr;
}
};
void SomeFunc(Sample x)
{
cout << "Say i am in someFunc " << endl;
}
int main()
{
Sample s1= 10;
SomeFunc(s1);
s1.PrintVal();
}
它返回输出,如:
Say i am in someFunc
Null pointer assignment(Run-time error)
此处当对象通过值传递给SomeFunc时,当控件从函数返回时,将调用该对象的析构函数
我应该对吗?如果是,那为什么会发生?什么解决方案????答案 0 :(得分:5)
您通常需要遵守 Rule of Three ,因为您有指针成员。
在您的代码示例中,为了避免您看到的未定义行为:
在必须的第一个语句中替换需要。
答案 1 :(得分:5)
Sample
按值传递给SomeFunc
,表示复制。副本具有相同的ptr
,因此当SomeFunc
返回时销毁该副本时,将删除两个对象的ptr
。然后当你在main中调用PrintVal()
时,你取消引用这个无效指针。这是未定义的行为。即使有效,那么当s1
被销毁时ptr
会再次被删除,这也是UB。
此外,如果编译器无法忽略Sample s1= 10;
中的副本,那么s1
甚至无法开始有效,因为当临时被销毁时,指针将被删除。大多数编译器确实避免使用此副本。
您需要正确实施复制或禁止复制。此类型的默认copy-ctor不正确。我建议将此类型设置为值类型(直接保存其成员而不是指针),以便默认的copy-ctor可以工作,或者使用智能指针来保存引用,以便它可以管理引用资源你和默认的copy-ctor仍然有用。
我真正喜欢C ++的一个原因是它对于在任何地方使用值类型非常友好,如果你需要一个引用类型,你可以在智能指针中包装任何值类型。我认为这比具有值语义的原始类型的其他语言要好得多,但是默认情况下用户定义的类型具有引用语义。
答案 2 :(得分:1)
由于SomeFunc()
按值获取其参数,因此会复制传递给它的Sample
对象。当SomeFunc()
返回时,临时副本将被销毁。
由于Sample
没有定义复制构造函数,因此其编译器生成的复制构造函数只是复制指针值,因此两个Sample
实例都指向相同的int
。当一个Sample
(临时副本)被销毁时,int
被删除,然后当第二个Sample
(原始)被销毁时,它会尝试删除相同的int
SomeFunc()
再次。这就是你的程序崩溃的原因。
您可以更改void someFunc(Sample const &x)
以取代引用,避免使用临时副本:
Sample
和/或您可以为int
定义一个复制构造函数,它分配一个新的{{1}},而不是仅仅将指针复制到现有的。{/ p>
答案 3 :(得分:0)
当您传递函数的参数时,它被称为复制构造函数,但是您没有它,因此指针未初始化。当它退出函数时,对象调用析构函数来删除单元化指针,因此它会产生错误。
答案 4 :(得分:0)
而不是
int main()
{
Sample s1= 10;
SomeFunc(s1);
s1.PrintVal();
}
尝试使用
int main()
{
Sample* s1= new Sample(10);
SomeFunc(*s1);
s1->PrintVal();
}