Почему был вызван конструктор копирования? - PullRequest
3 голосов
/ 26 апреля 2020

В следующем коде я создаю 4 объекта с именами p1, p2, p3 и copy класса player и печатаю их атрибуты, используя while l oop , для которого код и вывод следующие. Но я ожидал другой вывод, и я не знаю, где я сделал копию объектов в первых 3 случаях.

#include <iostream>
using namespace std;
class player{
    public:
    int xp;
    string name;
    int health;

    player():player(0,0,"none") {} 
    player(int a):player(a,0,"none") {} 
    player (int a, int b, string c):name{c},xp{a},health{b} {}
    player (player &source)
    {
        name="copied player";
        xp=-1;
        health=-1;
    }
};
int main()
{
    player p1;
    player p2(2);
    player p3(2,5,"play3");
    player copy{p2};
    player arr[4]{p1,p2,p3,copy};
    int t=4;
    while(t--)
    {
        cout<<arr[3-t].name<<endl;
        cout<<arr[3-t].xp<<endl;
        cout<<arr[3-t].health<<endl;
    }
}

Для которых я получаю следующий вывод:

copied player
-1
-1
copied player
-1
-1
copied player
-1
-1
copied player
-1
-1

Однако я ожидал:

none
0
0
none
2
0
play3
2
5
copied player
-1
-1

Что я не знаю?

1 Ответ

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

Поскольку ваш код стоит (и как указано в комментариях), когда вы инициализируете массив arr[4], компилятор скопирует каждый объект в списке инициализатора на цель - отсюда вызов конструктор копирования четыре раза.

Один из способов избежать этого - использовать std::move(x) в списке инициализатора, но для этого вам нужно будет предоставить конструктор перемещения для вашего player класса (в вашем случае будет достаточно по умолчанию).

Однако помните, что после перемещения от объекта исходный объект больше не обязательно будет тем же самым как это было и использование его может быть недействительным. Единственное требование после перемещения (хотя класс может дать больше гарантий), чтобы объект находился в состоянии, когда его можно безопасно уничтожить. (Спасибо за комментарий от Jesper Juhl к этой заметке!)

Этот код даст ожидаемый результат:

#include <iostream>
#include <utility> // Defines std::move()
using std::string;
using std::cout; using std::endl;

class player {
public:
    int xp;
    string name;
    int health;

    player() :player(0, 0, "none") {}
    player(int a) :player(a, 0, "none") {}
    player(int a, int b, string c) :name{ c }, xp{ a }, health{ b } {}
    player(player& source) {
        name = "copied player";
        xp = -1;
        health = -1;
    }
    player(player&& p) = default; // Use the compiler-generated default move c'tor
};

int main()
{
    player p1;
    player p2(2);
    player p3(2, 5, "play3");
    player copy{ p2 };
//    player arr[4]{ p1,p2,p3,copy };
    player arr[4]{ std::move(p1), std::move(p2), std::move(p3), std::move(copy) };
    int t = 4;
    while (t--) {
        cout << arr[3 - t].name << endl;
        cout << arr[3 - t].xp << endl;
        cout << arr[3 - t].health << endl;
    }
    return 0;
}

Примечание: Пожалуйста, прочитайте также: Почему "использование пространства имен std;" считается плохой практикой? .

...