Страницы

Поиск по вопросам

вторник, 26 февраля 2019 г.

Конструктор по умолчанию

class A { public: A():a(0),b(0) {} explicit A(int x): a(x), b(0) {} A(int x, int y): a(x), b(y) {} private: int a,b; };
и
class A { public: explicit A(int x=0, int y=0): a(x), b(y) {} private: int a,b; };
Есть ли различия? Что лучше использовать?


Ответ

Эти два объявления классов не эквивалентны.
Во втором объявлении класса конструктор объявлен со спецификатором функции explicit, а это ограничивает применение этого конструктора в различных ситуациях.
В первом же объявлении класса только конструктор преобразования объявлен со спецификатором функции explicit. А это означает, что другие конструкторы вы можете вызывать неявно.
То есть первое объявление предоставляет больше возможностей по использованию класса.
Рассмотрите следующую демонстрационную программу
#include
struct A { explicit A( int x = 0, int y = 0 ) : x( x ), y( y ) {} int x; int y; };
struct B { B() : x( 0 ), y( 0 ) {} explicit B( int x ): x( x ), y( 0 ) {} B( int x, int y ): x( x ), y( y ) {} int x; int y; };
void f( const A &a ) { std::cout << "a.x = " << a.x << ", a.y = " << a.y << std::endl; }
void g( const B &b ) { std::cout << "b.x = " << b.x << ", b.y = " << b.y << std::endl; }
int main() { // f( {} ); // f( { 1, 2 } );
g( {} ); g( { 1, 2 } ); }
Ее вывод на консоль:
b.x = 0, b.y = 0 b.x = 1, b.y = 2
В этой программе два вызова функции f закомментированы, так как если их раскомментировать, то компилятор выдаст сообщение об ошибке.
Другое важное отличии состоит в том, что один класс имеет всего лишь один конструктор с заданной сигнатурой, а другой класс имеет три конструктора с различными сигнатурами.
Рассмотрите еще один демонстрационный пример
struct A { explicit A( int x = 0, int y = 0 ) : x( x ), y( y ) {} int x; int y; };
struct B { B() : x( 0 ), y( 0 ) {} explicit B( int x ): x( x ), y( 0 ) {} B( int x, int y ): x( x ), y( y ) {} int x; int y; };
struct C { //friend A::A(); friend B::B(); };
int main() { }
Здесь в классе C вы можете объявить конструктор по умолчанию класса B в качестве друга класса С. Однако вы не можете сделать то же самое с конструктором по умолчанию класса A, чтобы объявить его другом класса C, так как конструктор по умолчанию в классе A имеет другую сигнатуру.
Вам уже придется писать
struct C { friend A::A( int, int ); };
а это может быть не тем, что вы хотели бы получить. То есть если вы, например, хотели, чтобы другом был конструктор, который вызывается исключительно без аргументов.
То есть, опять-таки, когда имеются отдельные конструкторы, то ваши возможности более широкие.
Если рассматривать не конструкторы, а функции, то разница имеется еще более существенная.
Аргументы по умолчанию не влияют на тип функции. Поэтому, например, если вы объявили функцию как
void f( int, int = 0 );
то, несмотря на аргумент по умолчанию и того факта, что вы можете ее вызывать как
f( value );
тем не менее ее тип void( int, int ). А это в свою очередь означает, что вы не можете, например, написать
void h( void f( int ) ) { f( 10 ); }
void f( int x, int y = 0 ) { std::cout << "x = " << x << ", y = " << y << std::endl; }
//
h( f );
так как параметр функции h имеет тип void( int )., а у функции, используемой в качестве аргумента, тип void( int, int )
Если же вы объявите две функции вместо одной
void h( void f( int ) ) { f( 10 ); }
void f( int x ) { std::cout << "x = " << x << std::endl; } void f( int x, int y ) { std::cout << "x = " << x << ", y = " << y << std::endl; }
то данный вызов
h( f );
будет корректным, так как имеется функция с одним параметром.

Комментариев нет:

Отправить комментарий