Иногда можно встретить утверждение, что неявно определенный компилятором конструктор копирования эквивалентен следующему определенному пользователем конструктору копирования (будем считать, что класс позволяет определить неявно конструктор копирования с параметром в виде константной ссылки на объект класса) в тех случаях, когда класс не имеет собственных членов данных:
inline SomeClass( const SomeClass & ) {}
Например при таком определении класса
struct A
{
A() {}
A( const A & ) {}
};
Однако на самом деле эти определения не эквиваленты, если класс имеет подобъект в виде базового класса Различие заключается в том, что определенный неявно компилятором конструктор копирования осуществляет копирование подобъекта базового класса, в то время как определенный пользователем таким образом, как показано выше, конструктор копирования не выполняет копирование базового класса а использует инициализацию своего подобъекта по умолчанию. Незнание этого может привести к трудно находимым ошибкам.
Приведу соответствующие выдержки из стандарта
C++, прежде чем показать пример, демонстрирующий это различие.
В параграфе №15 раздела 12.8 Copying and moving class objects написано относительно неявно определенного конструктора копирования:
цитата: |
15 The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move of its bases and members. |
|
Что касается явно определенного пользователем конструктора копирования, то механизм создания подобъектов класса другой. Он описан в параграфе №8 раздела 12.6.2 Initializing bases and members стандарта
C++:
цитата: |
8 In a non-delegating constructor, if a given non-static data member or base class is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then — if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initialized as specified in 8.5; — otherwise, if the entity is an anonymous union or a variant member (9.5), no initialization is performed; — otherwise, the entity is default-initialized (8.5). |
|
Наглядно продемонстрировать это различие можно на следующем простом примере:
#include <iostream>
struct A
{
A() : i( 0 ) { std::cout << "A::A()" << std::endl; }
A( int i ) : i( i ) {}
A( const A &rhs ): i( rhs.i ) { std::cout << "A::A( const A & )" << std::endl; }
int i;
};
struct B : A
{
B( int i = 0 ) : A( i ) {}
B( const B & ){}
};
struct C : A
{
C( int i = 0 ) : A( i ) {}
C( const C & ) = default;
};
int main()
{
B b1( 10 );
B b2( b1 );
std::cout << "b2.i = " << b2.i << std::endl;
C c1( 10 );
C c2( c1 );
std::cout << "c2.i = " << c2.i << std::endl;
return 0;
}
В этом примере класс B имеет явно определенный пользователем конструктор копирования, а класс C имеет неявно определенный компилятором конструктор копирования.
Результатом работы этого примера будет следующий вывод на консоль:
A::A()
b2.i = 0
A::A( const A & )
c2.i = 10
который наглядно демонстрирует различие в работе этих двух конструкторов копирования.
Этот пример можно использовать как тестовое задание, то есть представить код и спросить, каков будет вывод на консоль. Если программист знает различие между неявно определенным и явно определенным конструкторами копирования, то он даст правильный ответ. Если же не знает, то ответ будет неверным.