Как сделать глубокое или поверхностное копирование в зависимости от модификатора ссылки? - PullRequest
2 голосов
/ 22 апреля 2020

Допустим, у меня есть класс А с внутренними указателями. Я объявляю переменные:

A a;

A b;

A & c;

Я хотел бы "a = b" (или "b = a ") сделать глубокую копию, но я бы хотел, чтобы" c = a "(или" c = b ") действовал как ссылка и отражал изменения, внесенные в a или b в будущем.

Я бы подумал, что поверхностное поведение копирования / ссылки для c было автоматическим c, но я думаю, что я мог плохо перегружать свой оператор присваивания, потому что он вызывается, когда я говорю "c = a" и по-прежнему выполняет глубокое копирование.

Существующая подпись оператора присваивания: A & operator = (const A & a);

Как использовать перегруженный оператор присваивания для глубокого копирования, но поддерживать поведение, подобное ссылочному для справок?

Ответы [ 3 ]

3 голосов
/ 22 апреля 2020

C ++ хочет, чтобы ссылка действовала как просто другое имя для объекта, с которым была связана ссылка. Он , в частности , не хочет, чтобы пользователи могли связывать ссылки, чтобы привязанная ссылка ссылалась на другой объект.

Таким образом, если c является ссылкой на a , тогда каждое использование c будет вести себя точно так же, как если бы вы использовали a (с некоторыми небольшими исключениями, ни одно из которых не применимо для вашего использования). Вы не можете «поддерживать ссылки как поведение для ссылок»; это просто не то, для чего нужна языковая функция.

Вам нужен указатель (или std::reference_wrapper, но указатели, как правило, то, что вам нужно). Таким образом, вы можете провести различие между операцией повторного связывания (c = &a) и операцией присвоения значения (*c = a).

3 голосов
/ 22 апреля 2020

Это не так, как ссылки работают в C ++. Ссылка связана с другой переменной только во время объявления. На самом деле этот код

A& c;

даже не скомпилируется. Вы должны связать его с какой-то другой переменной, например:

A& c = a;  // does not call operator= (no deep copy)

Однако после этого момента присвоение c вызовет оператор назначения копирования, если вы выполните c = b;, и это сделает глубокую копию. Обратите внимание, что поскольку c связан с a, это то же самое, что присвоение b a.

1 голос
/ 22 апреля 2020

A & c;

Это не то, как мы определяем переменную reference в C ++. Ссылка должна быть привязана к объекту в точке их определения. После их создания их нельзя изменить, чтобы они указывали на что-то еще.

Правильный способ сделать это:

A&c = a;

Для вышеприведенного пункта не вызывается конструктор. Просто c является псевдонимом для объекта a. Однако, если вы сделаете c = b, тогда будет вызываться copy assignment для объектов a и b, как вы можете видеть ниже.

Я бы подумал, что мелкая копия / ссылка поведение для c было автоматическим c, но я думаю, что, возможно, я плохо перегрузил свой оператор присваивания, потому что он вызывается, когда я говорю "c = a", и все еще делает глубокую копию.

c = a вызывает конструктор копирования объекта a и b. Указатель в C ++ будет лучшим вариантом для вас. Их можно изменить, чтобы они указывали на любой объект. См. Мой пример для указателей в функции main ниже, и она делает копию shallow (см. Не вызывается конструктор или оператор присваивания).

Пример, показывающий, что вызывается для какого объекта:

#include <iostream>

class A {

public:
    A(void)
    {
        std::cout << "__CONSTRUCTOR__" << std::endl;
        std::cout << this << std::endl;
    }
    A(const A& )
    {
        std::cout << "__COPY_CONSTRUCTOR__" << std::endl;
        std::cout << this << std::endl;
    }
    const A& operator=(const A& other)
    {
        std::cout << "__COPY_ASSIGNMENT__" << std::endl;
        std::cout << this << " and " << &other <<  std::endl;
        return *this;
    }
    ~A(void)
    {
        std::cout << "__DESTRUCTOR__" << std::endl;
        std::cout << this << std::endl;
    }
};


int main(void)
{
    std::cout << "Example with reference" << std::endl;
    std::cout << "------------ CREATING A ------------" << std::endl;
    A a;
    std::cout << "------------ CREATING B ------------" << std::endl;
    A b;
    std::cout << "------------ CREATING C ------------" << std::endl;
    A& c = b;
    std::cout << "-------------- C = A ---------------" << std::endl;
    c = a;


    std::cout << std::endl;
    std::cout << std::endl;

    std::cout << "Example with pointers" << std::endl;
    std::cout << "------------ CREATING D ------------" << std::endl;
    A *d;
    std::cout << "-------------- D = &A --------------" << std::endl;
    d = &a;
    std::cout << "-------------- D = &B --------------" << std::endl;
    d = &b;

    std::cout << std::endl;
    std::cout << std::endl;
    std::cout << "Leaving the program" << std::endl;
}

Вывод:

Example with reference
------------ CREATING A ------------
__CONSTRUCTOR__
0x7fffe46d4ed6
------------ CREATING B ------------
__CONSTRUCTOR__
0x7fffe46d4ed7
------------ CREATING C ------------
-------------- C = A ---------------
__COPY_ASSIGNMENT__
0x7fffe46d4ed7 and 0x7fffe46d4ed6


Example with pointers
------------ CREATING D ------------
-------------- D = &A --------------
-------------- D = &B --------------


Leaving the program
__DESTRUCTOR__
0x7fffe46d4ed7
__DESTRUCTOR__
0x7fffe46d4ed6
...