- 此问题在此处已有答案**:
What is The Rule of Three?(8个答案)
昨天关闭。
#include <iostream>
using namespace std;
class samp
{
int *p;
int len;
int idx; // idx denotes the number of elements currently in the array (allocated by p)
public:
samp()
{
len=0;
p=NULL;
idx=0;
cout<<"inside default constructor"<<endl;
}
samp(int len)
{
this->len=len;
p=new int[len];
idx=0;
cout<<"inside parameterized constructor, p="<<p<<endl;
}
samp(const samp &s)
{
len=s.len;
idx=s.idx;
p = new int[len];
for(int i=0;i<idx;i++){
p[i]=s.p[i];
}
cout<<"inside copy constructor, p="<<p<<endl;
}
~samp()
{
cout<<"inside destructor, clearing memory "<<p<<endl;
delete[] p;
p=NULL;
}
void insert(int a)
{
if(idx<len){ // if there is still some place left in the array
p[idx]=a;
idx++;
}
}
void print()
{
for(int i=0;i<idx;i++){
cout<<p[i]<<" ";
}
cout<<endl;
}
};
samp f()
{
samp s(3);
int a;
for(int i=0;i<3;i++){
cin>>a;
s.insert(a);
}
cout<<"now we will return an object from a function"<<endl;
return s;
}
int main()
{
samp s1;
cout<<"now we will try to return an object from a function"<<endl;
s1 = f();
cout<<"function returned"<<endl;
s1.print();
return 0;
}
我得到了下面的输出和一个double free错误,因为相同的内存被释放了两次。由于复制省略,这里没有调用复制构造函数。
inside default constructor
now we will try to return an object from a function
inside parameterized constructor, p=0x5573aa164e80
now we will return an object from a function
inside destructor, clearing memory 0x5573aa164e80
function returned
0 0 -1441456112
inside destructor, clearing memory 0x5573aa164e80
由于复制构造函数没有被调用,所以我也得到了垃圾值。
当我使用-fno-elide-constructors
作为编译器标志时,复制构造函数被调用,但释放双倍内存错误仍然存在。
inside default constructor
now we will try to return an object from a function
inside parameterized constructor, p=0x10c008
now we will return an object from a function
inside copy constructor, p=0x10c040
inside destructor, clearing memory 0x10c008
inside destructor, clearing memory 0x10c040
function returned
85 23 19
inside destructor, clearing memory 0x10c040
代码块设法打印存储在释放的内存中的值,但是同一内存被释放两次,这是我希望避免的。
这里析构函数在函数返回时被调用了两次,很可能是因为它先析构函数f的局部对象s,然后再析构复制对象。
如果我们重载=运算符,那么释放双精度的问题就消失了。
samp& operator=(const samp &s)
{
len=s.len;
idx=s.idx;
p = new int[len];
for(int i=0;i<idx;i++){
p[i]=s.p[i];
}
cout<<"assigning, new p="<<p<<endl;
return *this;
}
然后我会得到这样的输出:
inside default constructor
now we will try to return an object from a function
inside parameterized constructor, p=0x6bc008
now we will return an object from a function
inside copy constructor, p=0x6bc040
inside destructor, clearing memory 0x6bc008
assigning, new p=0x6bc078
inside destructor, clearing memory 0x6bc040
function returned
85 23 19
inside destructor, clearing memory 0x6bc078
我的问题是,如果一个复制构造函数不能解决一个内存位置被释放两次的问题,为什么还要在从函数返回一个对象时调用它呢?而且,=操作符重载和复制构造函数的代码几乎是一样的。重载的=操作符是如何解决这个问题的呢?
如果我从一个函数返回一个对象,很有可能在函数调用后把它赋值给另一个对象,那么我们是否需要在每次从函数返回一个动态分配内存的对象时重载=操作符呢?
我知道STL向量。我需要知道复制构造函数在这种情况下的行为。
1条答案
按热度按时间u5rb5r591#
你混淆了复制构造函数和赋值操作符。为了得到正确的RAII和正确处理分配的内存,必须定义这两个函数。
这就调用了赋值运算符,正是缺少赋值运算符导致了这里的double free,无论如何,复制构造函数绝对与它无关。
你的复制构造函数做得很好,当从函数返回时,一个非省略的副本总是调用复制构造函数,如果一个副本被省略,什么也不会被复制,所以不涉及复制构造函数。
但是不管复制是否被省略,这总是会导致赋值运算符被调用。这是将返回值赋值给一个现有的对象。句号。这需要调用赋值运算符。当将一个对象赋值给另一个对象时,复制构造函数是完全无关的。实现它的失败会导致双重释放。