Rak*_*noy 9 c++ pointers copy-constructor
任何人都可以解释*p=*q这个C++代码的含义吗?这是一个复制构造函数的概念吗?
class A{
//any code
}
int main(){
A *p=new A();
A *q=new A();
*p=*q;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
Arn*_*rah 13
这是一个复制构造函数的概念吗?
不,你指的是复制任务概念.考虑一下:
int* p = new int{9};
int* q = new int{10};
*p = *q;
Run Code Online (Sandbox Code Playgroud)
如您所见,仅q复制指向的变量的值.这相当于对象的复制分配.如果你这样做:
p = q;
Run Code Online (Sandbox Code Playgroud)
然后这不是复制赋值,因为它们都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;
}
Run Code Online (Sandbox Code Playgroud)
这是一个补充反例
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
}
Run Code Online (Sandbox Code Playgroud)
如您所见,这些变化反映在两个变量上 p=q
附注 你提到了复制构造,但你不清楚这个概念.这是复制结构的样子:
int* p = new int{9};
int* q = new int{*p}; //q=9
Run Code Online (Sandbox Code Playgroud)
复制构造与复制分配的不同之处在于,对于复制构造,变量尚未具有值,对于对象,构造函数尚未被调用.混合使用这两个术语是很常见的,但根本的区别在于这两个概念是完全不同的.
看起来,您不清楚复制构造函数和复制赋值.让我们先来看看这两个概念,然后我会回答你的问题.答案有点长,所以请耐心等待:)
在这里,我不打算解释如何编写复制构造函数,但是当调用复制构造函数时,它不会.(如果你想知道,如何编写一个拷贝构造函数,请看这个)
复制构造函数是一种特殊的构造函数,用于将新对象创建为现有对象的副本.(只要需要制作现有对象的副本,就会调用它)
这些是复制构造函数将被调用以生成现有对象的副本的场景:
使用一些先前创建的对象初始化对象:
SomeClass obj;
// ...
SomeClass anotherObj = obj; // here copy constructor will be called.
Run Code Online (Sandbox Code Playgroud)
看,SomeClass obj;语句只是创建一个对象(这里,默认构造函数将被调用来创建对象).第二个语句SomeClass anotherObj = obj;是实例化一个对象,用obj(现有对象)的值初始化,因此这里将调用复制构造函数.您也可以这样使用现有对象初始化对象:( SomeClass anotherObj(obj);此语句相当于SomeClass anotherObj = obj;)
除外:
如果使用某个rvalue表达式初始化.例如
SomeClass someObject = aObject + anotherObject;
Run Code Online (Sandbox Code Playgroud)
在这种情况下,将调用移动构造函数.看,什么是移动语义?
按值将对象传递给某个函数(请参阅按值传递参数):
请参阅以下代码片段,此处函数doSomething通过值接受对象作为参数:
void doSomething(SomeClass someObject)
{
// ...
}
Run Code Online (Sandbox Code Playgroud)
在某些情况下,当需要在参数对象中制作传递参数的副本时someObject,我列出了何时需要制作副本以及何时不需要.
看看下面的代码片段:
SomeClass someObject;
// ...
doSomething(someObject); // here copy constructor will be called.
Run Code Online (Sandbox Code Playgroud)
该语句SomeClass someObject;只是someObject通过调用默认构造函数来实例化.
第二个语句通过传递参数doSomething(someObject);来调用doSomething先前显示的函数someObject.当需要制作someObject传递给函数的副本时就是这种情况.
除了:
Similiary,如果我们调用doSomething一些rvalue表达式,它将调用move构造函数而不是copy构造函数.
按值从函数返回对象:
我们来看看下面的定义 doSomething
SomeClass doSomehing()
{
SomeClass someObject;
// ...
return someObject;
}
Run Code Online (Sandbox Code Playgroud)
在上面的函数中doSomething,SomeClass正在创建一个对象,在完成一些任务之后,该函数返回该对象,在这种情况下,someObject将创建并返回该对象的副本.
除了:
Similiary,如果doSomething返回一些rvalue表达式,它将调用move构造函数而不是copy构造函数.
复制分配通常与复制构造混淆,让我们看看它与复制构造的不同之处:
SomeClass someObject;
// ...
SomeClass anotherObject;
// ...
anotherObject = someObject; // here copy assignment operator will be called.
Run Code Online (Sandbox Code Playgroud)
前两个语句只是创建someObject和anotherObject,你看,第三条语句实际上是调用拷贝赋值运算符,并没有拷贝构造函数.
只有在创建一些新对象时才会调用构造函数.在这种情况下anotherObject = someObject;,两个对象都已创建,因此不会对复制构造函数进行任何调用.而是调用复制赋值运算符(要查看如何重载复制赋值运算符,请参阅此处)
现在,让我们来看看你的代码片段:
Run Code Online (Sandbox Code Playgroud)A *p=new A(); A *q=new A(); *p=*q;
在第一条语句A *p=new A();,默认的构造函数将被调用,以创建一个对象(在这种情况下,新对象将在堆上创建)和p将与新创建的对象的地址进行初始化(作为p一个指针)
与第二个语句类似A *q=new A();(它正在创建另一个对象,q并将使用新创建的对象进行初始化)
现在,第三个声明:( *p = *q;这里*是Dereference运算符)
要理解第三个语句正在做什么,让我们看看一些指针并取消引用它们以获得它们指向的实际对象.
int someVariable = 5;
int *somePointer = &someVariable;
// ...
*somePointer = 7;
Run Code Online (Sandbox Code Playgroud)
让我们尝试理解上面的代码片段:someVariable使用值创建并初始化5,然后somePointer创建并使用地址初始化someVariable.
现在,最后一个语句*somePointer = 7;,它实际上是取消引用somePointer和通过取消引用,它将获得它指向的变量.(所以它会得到someVariable)然后分配7给它.所以在这个陈述之后,someVariable价值就会变成7
我们有另一个例子:
int* somePointer = new int;
int* anotherPointer = new int;
// ...
*somePointer = 5;
*anotherPointer = 7;
// ...
*somePointer = *anotherPointer;
Run Code Online (Sandbox Code Playgroud)
首先,somePointer将使用动态分配的int变量的地址创建和初始化(将在堆中分配,请参阅c ++中的动态分配),类似地,anotherPointer将使用另一个动态分配的变量的地址进行初始化.
然后,它分配5给第一个变量(正在被指向somePointer)和7第二个变量(正在被指向anotherPointer)
现在,最后一个语句将是您感兴趣的,*somePointer = *anotherPointer;在此语句中*somePointer是取消引用并获取第一个变量(其值为5)并且*anotherPointer正在取消引用并获取第二个变量(其值为7),并且它正在分配第二个变量到第一个变量,导致第一个变量的值改为7.
现在,让我们看看你的*p=*q;语句,(p指向第一个对象A,并q指向A动态分配的第二个对象),*p将取消引用p并获取第一个对象,*q将取消引用q并获取第二个对象,以及然后第二个对象将被复制到第一个对象中,如果你看到,没有创建新对象*p=*q;,只在第一个对象中创建了第二个对象的值,所以这里将调用复制赋值运算符而不是复制构造函数.
另一件事
您应该使用new运算符取消分配您借用的动态分配的内存:
delete p;
delete q;
Run Code Online (Sandbox Code Playgroud)
您应该在程序结束时添加这两行,以避免内存泄漏.
如评论中所述,首先需要了解基础知识:
与A *p=new A();一个得到一个指针,指向的存储区域上,其中类型的对象堆A构造.
使用*p一个解引用指针,即一个检索对象.
现在*p = *q使用类的(可能是隐式声明的)赋值运算符,A以便给出*p- 指向的对象p- 的值*q.此操作可以等效地写为p->operator=(*q).
最后一步,即赋值,与使用对象而不是指针获得的内容相同(这通常是使用C++并且通常称为RAII的更好方式):
A r;
A s;
r=s;
Run Code Online (Sandbox Code Playgroud)