2016-09-04 4 views
8

Может ли кто-нибудь объяснить значение *p=*q в этом коде на C++? Является ли это концепцией конструктора копирования?C++ copy constructor с помощью указателей

class A{ 
    //any code 
} 

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

You «Дайвинг слишком глубоко. Обязательно сначала поймите основы. Ситуация такая же, как в 'int a;' 'int * p = & a, * q = & a;' '* p = * q;' –

+1

@KerrekSB Но в коде OP 'new A()' и 'new A() 'производят разные экземпляры, тогда как ваша аналогия использует единственный экземпляр' int a'. Я думаю, что лучшей параллелью было бы: ** 'int a1 = 5, a2 = 10;' int * p = &a1; '' int * q = &a2; '' * p = * q; '** –

+0

Для любого указателя' tp', который имеет тип 'T *', выражение '* tp' будет разыменовывать этот указатель, оценивая значение типа' T', на которое указывает 'tp'. Итак, вы можете прочитать это как «значение, на которое указывает« p »= значение, на которое указывает« q »;' –

ответ

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

Side Примечание вы упомянули от копирования конструкции, но были неясны о концепции. Вот то, что копирование строительство выглядело бы как:

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

Copy конструкция отличается от присвоения копии в том смысле, что при строительстве копии, переменные уже не имеет значения, и для объекта, конструктор имеет но еще не назовешь. Смешивание двух терминов является общим, но фундаментальные различия делают две концепции, ну, разные.

+0

Вы забыли точки с запятой до конца обоих 'main' – Ruslan

+0

Il edit t прямо сейчас –

6

Похоже, вы не знаете о конструкторе копирования и назначении копии. Давайте сначала рассмотрим обе концепции в отдельности, и тогда я приду к вашему вопросу. Ответ немного долго, так что будьте терпеливы :)

Copy Constructor

Здесь я не буду объяснять, как написать конструктор копирования, но когда конструктор копирования называется и когда это не , (Если вы хотите узнать, как написать конструктор копирования, см. this)

Конструктор копирования является специальным конструктором для создания нового объекта в качестве копии существующего объекта.(Это называется всякий раз, когда есть необходимость сделать копию существующего объекта)

Эти сценарии, когда конструктор копирования будет называться, чтобы сделать копию существующего объекта:


  • Инициализация объекта с некоторыми ранее созданного объекта:

    SomeClass obj; 
    // ... 
    SomeClass anotherObj = obj; // here copy constructor will be called. 
    

    Престола, SomeClass obj; заявление s подразумевает создание объекта (здесь будет вызываться default constructor, чтобы создать объект). Второй оператор SomeClass anotherObj = obj; создает экземпляр объекта, инициализированного значениями obj (существующий объект), поэтому здесь будет вызываться конструктор копирования. Кроме того, можно инициализировать объект с существующим объектом таким образом: SomeClass anotherObj(obj); (Это утверждение эквивалентно SomeClass anotherObj = obj;)

    Кроме:
    При инициализации с некоторыми RValue выражением. например

    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; просто someObject по создание экземпляра вызова default constructor.

    Второе заявление doSomething(someObject); вызывает функцию doSomething, ранее показанную, передав someObject в качестве аргумента. Это тот случай, когда необходимо передать копию someObject, чтобы перейти к функции.

    Кроме:
    Similiary, если мы называем doSomething с некоторыми RValue выражения, он будет вызывать двигаться конструктор вместо конструктор копирования.


  • Возвращение объекта из функции по значению:

    Давайте посмотрим на следующее определение doSomething

    SomeClass doSomehing() 
    { 
        SomeClass someObject; 
        // ... 
        return someObject; 
    } 
    

    В приведенной выше функции doSomething , объект SomeClass создается и после выполнения некоторой задачи объект возвращается функцией, в этом случае будет создана и возвращена копия someObject.

    Кроме:
    Similiary, если doSomething возвращает некоторые выражение Rvalue, он будет вызывать двигаться конструктор вместо конструктор копирования.


Copy Assignment

Копия Назначение обычно путают с конструкцией копирования, давайте посмотрим на то, как он отличается от конструкции копирования:

SomeClass someObject; 
// ... 
SomeClass anotherObject; 
// ... 
anotherObject = someObject; // here copy assignment operator will be called. 

Первый два заявления только создают someObject и anotherObject, видите ли, третий статет nt фактически вызывает оператор присваивания копии и не конструктор копирования.

Конструкторы вызываются только при создании какого-либо нового объекта. А в случае anotherObject = someObject; оба объекта уже созданы, поэтому вызов конструктора копирования не будет.Вместо того, чтобы оператор присваивания копии будет называться (чтобы увидеть, как перегрузить оператор присваивания копировального см this)


Теперь давайте посмотрим на фрагмент кода:

A *p=new A(); 
A *q=new A(); 
*p=*q; 

В первом заявлении A *p=new A(); по умолчанию будет создан конструктор по умолчанию для создания объекта (в этом случае новый объект будет создан в куче), а p будет инициализирован адресом вновь созданного объекта (как p является pointer)

Похожего дело со вторым утверждением A *q=new A(); (Это создает другой объект и q будет инициализирован с вновь созданным объектом)

Теперь, третье заявление: *p = *q; (здесь * является Dereference operator)

Чтобы понять, что делает третий оператор, давайте посмотрим на некоторые указатели и удалим их, чтобы получить фактический объект, на который они указывают.

int someVariable = 5; 
int *somePointer = &someVariable; 
// ... 
*somePointer = 7; 

Давайте попробуем понять выше фрагмент кода: someVariable создается и инициализируется со значением 5, то somePointer создается и инициализируется с адресом someVariable.

Теперь последнее заявление *somePointer = 7;, оно фактически отменяет ссылку на somePointer и путем де-ссылки, оно получит переменную, на которую она указывает. (поэтому он получит someVariable), а затем он назначает 7. Таким образом, после этого заявления, значение someVariable «s станет 7

Давайте еще один пример:

int* somePointer = new int; 
int* anotherPointer = new int; 
// ... 
*somePointer = 5; 
*anotherPointer = 7; 
// ... 
*somePointer = *anotherPointer; 

Во-первых, somePointer будет создан и инициализирован с адресом динамически распределяемой int переменной (будет выделена в куче , см. Dynamic Allocation in c++), аналогично, anotherPointer будет инициализирован адресом другой динамически назначенной переменной.

Затем он назначая 5 первой переменной (которая в настоящее время указываемой somePointer) и 7 второй переменной (которая в настоящее время указываемой anotherPointer)

Теперь, последнее утверждение будет представлять интерес , *somePointer = *anotherPointer;, в этом заявлении *somePointer де-ссылки и получение первой переменной (значение которой было 5), а *anotherPointer - удаление ссылок и получение второй переменной (значение которой равно 7), и она назначает вторую переменную первой переменная, что приводит к изменению значения первой переменной до 7.

Теперь давайте посмотрим на ваш *p=*q; заявлении (p указывал на первый объект A и q указывал на второй объект A, как динамически выделяемой), *p будет разыменовать p и получить первый object, *q будет разыменовывать q и получить второй объект, а затем второй объект будет скопирован в первом объекте, и если вы видите, новый объект не создается в *p=*q;, и создается только значение второго объекта в первый объект, поэтому оператор назначения копирования будет вызываться здесь, а не конструктор копирования.

Другое дело

Вы должны отменить выделять динамически выделенную память, вы заимствованные с помощью new оператора:

delete p; 
delete q; 

Вы должны добавить эти две строки в конце вашей программы, так избегать Memory Leak.

4

Как указано в комментариях, одна потребность понять основы первых:

  • С A *p=new A(); один получает указатель на область памяти в куче, в которой объект типа A построен.

  • С *p один из них разыскивает указатель, то есть извлекает объект.

  • Теперь *p = *q использует (возможно, неявно объявленный) оператор присваивания класса A для того, чтобы дать *p - объект, на который указывает p - значение *q. Эта операция может быть эквивалентно записана как p->operator=(*q).

Последний шаг, назначение, идентично тому, что вы получили бы с объектами вместо указателей (которые, как правило, лучше пойти в C++ и часто называют RAII):

A r; 
A s; 
r=s;