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 );
будет корректным, так как имеется функция с одним параметром.
Комментариев нет:
Отправить комментарий