Вызов конструктора C ++ и создание объекта - PullRequest
4 голосов
/ 17 сентября 2011
class Test{
    public :
        int x;  
        Test()
        {
            x = 0;
            cout<<"constructor with no arguments called"<<endl;
        }
        Test(int xx)
        {
            x = xx;
            cout<<"constructor with single int argument called"<<endl;
        }

};


int main()
{
    Test a(10);
    Test aa = 10;
}

Выход: Программа компилирует и выводит

конструктор с одним аргументом int с именем

конструктор с одним аргументом int с именем

Но сейчас

class Test{
    public :
        int x;  
        Test()
        {
            x = 0;
            cout<<"constructor with no arguments called"<<endl;
        }
        Test(int xx)
        {
            x = xx;
            cout<<"constructor with single int argument called"<<endl;
        }

        Test( Test& xx)
        {
            x = xx.x;
            cout<<"copy constructor called"<<endl;
        }


};


int main()
{
    Test a(10);
    Test aa = 10;
}

компиляция не удалась.

constructorinvokings.cc:36:7: error: no viable constructor copying variable of type 'Test'
        Test aa = 10;
             ^    ~~
constructorinvokings.cc:23:3: note: candidate constructor not viable: no known conversion from 'Test' to 'Test &' for 1st
      argument
                Test( Test& xx)
                ^
1 error generated.

Я новичок в C ++.

Не тестируют a (10) и тестируют aa = 10; идентичны?

почему добавление конструктора копирования конфликтует с Test aa = 10?

если я изменю Test (Test & xx) на Test (const Test & xx), он работает. Но почему компилятор проверяет сигнатуру конструктора копирования, когда мы пытаемся вызвать конструктор с целочисленным аргументом.

Пожалуйста, уточните

Заранее спасибо.

Ответы [ 4 ]

6 голосов
/ 18 сентября 2011

Все ответы, которые вы получили до сих пор, слишком сложны и / или немного вводят в заблуждение.

В первой конструкции / инициализации:

T a(10);

Происходит очень очевидная вещь.Вторая конструкция / инициализация более интересна:

T aa = 10;

Это эквивалентно:

T aa(T(10));

Это означает, что вы создаете временный объект типа T, а затем создаете aa каккопия этого временного.Это означает, что вызывается конструктор копирования.

C ++ имеет конструктор копирования по умолчанию, который он создает для класса, когда вы явно не объявили ни одного.Таким образом, хотя первая версия class T не имеет декларированного конструктора копирования, он все еще существует.Тот, который объявляет компилятор, имеет эту подпись T(const T &).

Теперь во втором случае, когда вы объявляете что-то, похожее на конструктор копирования, вы делаете аргумент T &, а не const T &.Это означает, что компилятор, пытаясь скомпилировать второе выражение, пытается использовать ваш конструктор копирования, а это невозможно.Он жалуется, что для конструктора копирования, который вы объявили, требуется неконстантный аргумент, а аргумент, который ему дается, является const.Так что это терпит неудачу.

Другое правило заключается в том, что после того, как компилятор преобразовал вашу инициализацию в T aa(T(10));, ему затем разрешается преобразовать его в T aa(10);.Это называется «копировать исключение».При определенных обстоятельствах компилятору разрешено пропускать вызовы конструктора копирования.Но он может сделать это только после того, как проверит, что выражение сформировано правильно и не генерирует никаких ошибок компилятора, когда вызов конструктора копирования все еще существует.Таким образом, это шаг оптимизации, который может точно повлиять на то, какие части программы выполняются, но не может повлиять на то, какие программы являются действительными, а какие - ошибочными (по крайней мере, с точки зрения того, компилируются они или нет).

4 голосов
/ 17 сентября 2011

Тест а (10) и Тест аа = 10;не идентичныПервый создает тест из 10, а второй создает тест из 10 и , а затем копирует конструкции из него.Добавление конструктора копирования конфликтует, поскольку в нем говорится, что создание конструкции копирования является изменяемой операцией для обоих двух операндов.Есть случаи, когда конструкторы копирования принимают источник как неконстантную ссылку, но это несколько сложные случаи, и это не ваш случай.

Edit: Обратите внимание, что во втором случае компиляторразрешено исключать эту конструкцию копирования, и обычно это так.

2 голосов
/ 18 сентября 2011

(Менее многословный тестовый пример здесь .)

Две формы инициализации на самом деле не полностью эквивалентны.

Первая формаваша стандартная конструкция болота:

T o(10);

Последнее, однако, более сложное.

T o = 10;

Здесь выражение RHS (10) преобразуется в тип T, то объект o создается из этого преобразованного выражения.Копирование здесь невозможно, поскольку ваш T(T&) запрещает синтез неявного T(T const&), а временный объект в RHS не может быть привязан к ref-to-non- const.

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


[n3290: 8.5/13]: Формаинициализация (использование скобок или =), как правило, незначительна, но имеет значение, когда инициализатор или инициализируемая сущность имеет тип класса;увидеть ниже.[..]

[n3290: 8.5/14]: Инициализация, которая происходит в форме

T x = a;

, а также при передаче аргумента, возвращении функции, вызове исключения (15.1), обработке исключения (15.3), и совокупная инициализация члена (8.5.1) называется copy-initialization .[ Примечание: Инициализация копирования может вызвать перемещение (12.8). —конечная заметка ]

[n3290: 8.5/16]: Семантика инициализаторов следующая.[..] Если тип назначения является (возможно, cv-квалифицированным) типом класса: [..] В противном случае (т. Е. для оставшихся случаев инициализации копирования ), определяемые пользователем последовательности преобразованиякоторые могут преобразовываться из исходного типа в тип назначения или (если используется функция преобразования) в его производный класс, перечисляются, как описано в 13.3.1.4 , и лучший выбирается с помощью разрешения перегрузки (13.3). Если преобразование не может быть выполнено или является неоднозначным, инициализация неверна. [..]

[n3290: 12.8/31]: При выполнении определенных критериев реализация может пропустить копию/ Переместить конструкцию объекта класса, даже если конструктор копирования / перемещения и / или деструктор для объекта имеют побочные эффекты.[..] Такое исключение операций копирования / перемещения, которое называется разрешением копирования, допускается при следующих обстоятельствах (которые могут быть объединены для удаления нескольких копий): [..]

  • при использовании временного классаобъект, который не был связан со ссылкой (12.2), будет скопирован / перемещен в объект класса с тем же типом cv-unqualified, операция копирования / перемещения может быть опущена путем создания временного объекта непосредственно в цель пропущенной копии/переехать.[..]

(На самом деле я не могу найти цитату, в которой явно говорится, что конструктор копирования все еще должен быть доступен, даже если копия исключена, но это так.)

0 голосов
/ 18 сентября 2011

Я не согласен с К-баллом. Когда вы определяете c'tor с одним аргументом, он может [mis] использоваться компилятором для неявного преобразования. Что и происходит в этом случае.

Таким образом, когда вы запускаете программу, она вызывает "c'tor with single arg" для построения aa aa.

Испытание аа = 10;

В этом утверждении нет «копирования» или «присвоения». Учитывая, что вы предоставили компилятору один аргумент arg c'tor, он в этом случае [неправильно] использовал бы его.

// So basically compiler converts this:
Test aa = 10;
// to
Test aa(10);

Вы можете остановить злоупотребление, пометив этот c'or как явный. Просто измените Test( int x ) на explicit Test( int x ) См. Некоторые объяснения явного ключевого слова здесь: Что означает явное ключевое слово в C ++?

если я изменю Test (Test & xx) на Test (const Test & xx), он работает. Но почему компилятор проверяет сигнатуру конструктора копирования, когда мы пытаемся вызвать конструктор с целочисленным аргументом.

Я тоже не понял этого и хотел бы знать. :)

Я заметил, что копия c'tor просто никогда не вызывается.

#include<iostream>
using namespace std;

class Test
{
public :
    int x;
    Test() { x=0; cout << "    Test() - " << x << endl;}
    Test(int xx) { x=xx; cout << "    Test(int xx) - " << x << endl;}
    Test(Test& xx){ x=xx.x; cout << "    Test(Test& xx) - " << x << endl;}
    Test(const Test& xx){ x=xx.x; cout << "    Test(const Test& xx) - " << x << endl;}
    Test& operator= (const Test& xx) { x=xx.x; cout << "    Test& operator= (const Test& xx) - " << x << endl; return *this;}
};


int main()
{
    cout << "--Test a(10);--" << endl;
    Test a(10);
    cout << "--Test aa = 20;--" << endl;
    Test aa = 20;
    cout << "--Test aaa = aa;--" << endl;
    Test aaa = aa;
    cout << "--aaa = aa;--" << endl;
    aaa = aa;
    cout << "--aaa = 30;--" << endl;
    aaa = 30;
}
/*
OUTPUT:
    --Test a(10);--
        Test(int xx) - 10
    --Test aa = 20;--
        Test(int xx) - 20
    --Test aaa = aa;--
        Test(Test& xx) - 20
    --aaa = aa;--
        Test& operator= (const Test& xx) - 20
    --aaa = 30;--
        Test(int xx) - 30
        Test& operator= (const Test& xx) - 30
*/

и когда копия c'tor с const arg Test(const Test& xx) закомментирована:

Мы получаем ошибки компиляции.

D:\Workspaces\CodeBlocks\Test\main.cpp: In function 'int main()':
D:\Workspaces\CodeBlocks\Test\main.cpp:21:19: error: no matching function for call to 'Test::Test(Test)'
D:\Workspaces\CodeBlocks\Test\main.cpp:10:9: note: candidates are: Test::Test(Test&)
D:\Workspaces\CodeBlocks\Test\main.cpp:9:9: note:                 Test::Test(int)
D:\Workspaces\CodeBlocks\Test\main.cpp:8:9: note:                 Test::Test()
Process terminated with status 1 (0 minutes, 0 seconds)
4 errors, 0 warnings

Строка 21 - Test aa = 20;

...