使用指针的C ++拷贝构造函数

时间:2016-09-04 13:06:36

标签: c++ pointers copy-constructor

有人能解释一下这个C ++代码中*p=*q的含义吗?这是一个复制构造函数概念吗?

class A{
  //any code
}

int main(){
  A *p=new A();
  A *q=new A();
  *p=*q;
  return 0;
}

3 个答案:

答案 0 :(得分:13)

  

这是一个复制构造函数概念吗?

不,你指的是复制作业概念。考虑一下:

int* p = new int{9};
int* q = new int{10};
*p = *q;

如上所示,只复制了指向的变量q的值。这相当于对象的复制分配。如果你这样做:

p = q;

然后这不是一个副本分配,因为int两个都指向相同的地址和值,这意味着对pq的任何更改都会被反映出来在另一个变量上。 为了给出一个更具体和验证的例子,这里有一些代码:

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;