有人能解释一下这个C ++代码中*p=*q
的含义吗?这是一个复制构造函数概念吗?
class A{
//any code
}
int main(){
A *p=new A();
A *q=new A();
*p=*q;
return 0;
}
答案 0 :(得分:13)
这是一个复制构造函数概念吗?
不,你指的是复制作业概念。考虑一下:
int* p = new int{9};
int* q = new int{10};
*p = *q;
如上所示,只复制了指向的变量q
的值。这相当于对象的复制分配。如果你这样做:
p = q;
然后这不是一个副本分配,因为int
两个都指向相同的地址和值,这意味着对p
或q
的任何更改都会被反映出来在另一个变量上。
为了给出一个更具体和验证的例子,这里有一些代码:
int main()
{
int* p = new int{9};
int* q = new int{10};
*p = *q;
//p = 10, q = 10
*p = 11;
//p = 11, q = 10
delete p;
delete q;
}
这是一个补充反例
int main()
{
int* p = new int{9};
int* q = new int{10};
p = q;
//p = 10, q = 10
*p = 11;
//p = 11, q = 11
delete p;
//delete q; Not needed because p and q point to same int
}
如您所见,更改反映在p=q
旁注 你提到了复制构造,但你不清楚这个概念。这是复制结构的样子:
int* p = new int{9};
int* q = new int{*p}; //q=9
复制构造与复制分配的不同之处在于,对于复制构造,变量不具有值,对于对象,构造函数尚未被调用。混合使用这两个术语是很常见的,但根本的区别在于这两个概念是完全不同的。
答案 1 :(得分:8)
看起来,你不清楚复制构造函数和复制赋值。让我们先来看看这两个概念,然后我会回答你的问题。答案有点长,所以请耐心等待:)。
<强> Copy Constructor 强>
在这里,我不打算解释如何编写复制构造函数,但是在调用复制构造函数时,以及何时不复制构造函数。 (如果您想知道,如何编写复制构造函数,请参阅this)
复制构造函数是一种特殊的构造函数,用于将新对象创建为现有对象的副本。 (无论何时需要制作现有对象的副本,都会调用它)
这些是调用复制构造函数来复制现有对象的场景:
使用以前创建的对象初始化对象:
SomeClass obj;
// ...
SomeClass anotherObj = obj; // here copy constructor will be called.
请参阅,SomeClass obj;
语句只是创建一个对象(这里,将调用default constructor来创建对象)。第二个语句SomeClass anotherObj = obj;
正在实例化一个对象,使用obj
(现有对象)的值进行初始化,因此将在此处调用复制构造函数。您还可以通过以下方式使用现有对象初始化对象:SomeClass anotherObj(obj);
(此语句等同于SomeClass anotherObj = obj;
)
<强>除强>:
如果使用某些 rvalue表达式初始化。 e.g。
SomeClass someObject = aObject + anotherObject;
在这种情况下,将调用移动构造函数。请参阅What are move semantics?
按值将对象传递给某个函数(参见Passing arguments by value):
请参阅以下代码段,此处函数doSomething
通过值接受对象作为参数:
void doSomething(SomeClass someObject)
{
// ...
}
在某些情况下,当需要在参数对象someObject
中复制传递的参数时,我会列出需要复制的时间以及何时复制那里没有必要。
查看以下代码段:
SomeClass someObject;
// ...
doSomething(someObject); // here copy constructor will be called.
声明SomeClass someObject;
只是通过调用default constructor来实例化someObject
。
第二个语句doSomething(someObject);
通过传递doSomething
作为参数来调用先前显示的函数someObject
。在这种情况下,需要复制someObject
以传递给函数。
<强>除强>:
Similiary,如果我们用{em> rvalue表达式调用doSomething
,它将调用移动构造函数而不是复制构造函数。
按值从函数返回对象:
让我们看一下doSomething
SomeClass doSomehing()
{
SomeClass someObject;
// ...
return someObject;
}
在上面的函数doSomething
中,正在创建SomeClass
的对象,在执行某项任务后,该函数返回该对象,在本例中为{{1}的副本将被创建并返回。
<强>除强>:
Similiary,如果someObject
返回一些 rvalue表达式,它将调用移动构造函数而不是复制构造函数。
<强> Copy Assignment 强>
复制作业通常与复制构造相混淆,让我们看看它与复制构造的不同之处:
doSomething
前两个语句只是创建SomeClass someObject;
// ...
SomeClass anotherObject;
// ...
anotherObject = someObject; // here copy assignment operator will be called.
和someObject
,您看,第三个语句实际上是调用复制赋值运算符而不是复制构造函数。
只有在创建一些新对象时才会调用构造函数。在anotherObject
的情况下,两个对象都已创建,因此不会对复制构造函数进行任何调用。而是调用复制赋值运算符(要查看如何重载复制赋值运算符,请参阅this)
现在,让我们看看您的代码段:
anotherObject = someObject;
在第一个语句A *p=new A();
A *q=new A();
*p=*q;
中,将调用默认构造函数来创建一个对象(在这种情况下,将在堆上创建新对象),A *p=new A();
将使用新的地址进行初始化已创建的对象(p
为<{3}})
类似于第二个语句p
的情况(它正在创建另一个对象,A *q=new A();
将使用新创建的对象进行初始化)
现在,第三个声明:q
(此处*p = *q;
为pointer)
要理解第三个语句正在做什么,让我们看看一些指针并取消引用它们以获得他们指向的实际对象。
*
让我们尝试理解上面的代码段:创建int someVariable = 5;
int *somePointer = &someVariable;
// ...
*somePointer = 7;
并使用值someVariable
进行初始化,然后创建5
并使用{的地址进行初始化{1}}。
现在,最后一个语句somePointer
,它实际上是取消引用someVariable
,并且通过取消引用,它将获得它指向的变量。 (因此它将获得*somePointer = 7;
)然后它会为其分配somePointer
。因此,在此声明之后,someVariable
的值将变为7
让我们举另一个例子:
someVariable
首先,7
将使用动态分配的int* somePointer = new int;
int* anotherPointer = new int;
// ...
*somePointer = 5;
*anotherPointer = 7;
// ...
*somePointer = *anotherPointer;
变量的地址创建并初始化(将在堆中分配,请参阅Dereference operator),类似地,somePointer
将用另一个动态分配的变量的地址初始化。
然后,它将int
分配给第一个变量(由anotherPointer
指向)和5
到第二个变量(由somePointer
指向)
现在,最后一个语句符合您的兴趣7
,在此语句中anotherPointer
正在取消引用并获取第一个变量(其值为*somePointer = *anotherPointer;
)和{{ 1}}取消引用并获取第二个变量(其值为*somePointer
),并将第二个变量赋值给第一个变量,从而将第一个变量的值更改为{{1 }}
现在,让我们看看您的5
语句,(*anotherPointer
指向7
的第一个对象,7
指向*p=*q;
p
的第二个对象(动态分配),A
将取消引用q
并获取第一个对象,A
将取消引用*p
并获取第二个对象,然后第二个对象将被复制到第一个对象中,如果你看到,p
中没有创建新对象,只在第一个对象中创建了第二个对象的值,所以复制赋值运算符将在此处调用,而不是复制构造函数。
另一件事
您应该使用*q
运算符取消分配您借用的动态分配内存:
q
您应该在程序结尾处添加这两行,以避免Dynamic Allocation in c++。
答案 2 :(得分:5)
正如评论中所述,首先需要了解基础知识:
使用A *p=new A();
获取指向堆上的内存区域的指针,其中构造了A
类型的对象。
使用*p
取消引用指针,即检索对象。
现在*p = *q
使用类A
的(可能是隐式声明的)赋值运算符,以便给*p
- p
指向的对象 - - *q
的值。此操作可以等效地写为p->operator=(*q)
。
最后一步,即赋值,与使用对象而不是指针获得的相同(这通常是使用C ++并且通常称为RAII的更好方式):
A r;
A s;
r=s;