2013-09-27 11 views
6

Я не понимаю, как constructors work?Почему конструктор вызывается дважды

Здесь я объявил объект obj2. Он вызывает конструктор abc(), что отлично.

Но когда я задаю

obj2 = 100 

Почему компилятор позволяет инициализировать целое число в объект класса? Если он вообще разрешает, то как он уничтожает объект, а затем как он вызывает другой параметризованный конструктор.

Теперь у меня есть еще один вопрос, почему destructor называется только один раз, так как есть two объектов?

Еще одно сомнение у меня есть, компилятор not doing anything с default constructor, то почему конструктор по умолчанию required?

class abc{ 
public: 
    int a, b; 

    abc() 
    {a = 0; b = 0;} 

    abc(int x) 
    {a = x;} 

    ~abc() 
    {std::cout << "Destructor Called\n";} 
}; 
int main() 
{ 
    abc obj1; 
    cout << "OBJ1 " << obj1.a << "..." << obj1.b << "\n"; 
    abc obj2; 
    cout << "OBJ2 " << obj2.a << "..." << obj2.b << "\n"; 
    obj2 = 100; 
    cout << "OBJ2 " << obj2.a << "\n"; 
system("pause"); 
return 0; 
} 

output:

OBJ1 0...0 
OBJ2 0...0 
Destructor Called 
OBJ2 100 

ответ

3

Давайте играть шоу и сказать, так что давайте инструмент всем специальным членам:

#include <iostream> 

class abc{ 
public: 
    int a, b; 

    abc() 
    { std::cout << "Default constructor\n"; a = 0; b = 0;} 

    abc(int x) 
    { std::cout << "Int constructor\n"; a = x;} 

    abc(abc const& other): a(other.a), b(other.b) 
    { std::cout << "Copy constructor (" << a << ", " << b << ")\n"; } 

    abc& operator=(abc const& other) { 
     std::cout << "Assignment operator (" << a << ", " << b << ") = (" << other.a << ", " << other.b << ")\n"; 
     a = other.a; 
     b = other.b; 
     return *this; 
    } 

    ~abc() 
    {std::cout << "Destructor Called\n";} 
}; 

int main() 
{ 
    abc obj1; 
    std::cout << "OBJ1 " << obj1.a << "..." << obj1.b << "\n"; 
    abc obj2; 
    std::cout << "OBJ2 " << obj2.a << "..." << obj2.b << "\n"; 
    obj2 = 100; 
    std::cout << "OBJ2 " << obj2.a << "\n"; 

    return 0; 
} 

И мы получаем this output:

Default constructor 
OBJ1 0...0 
Default constructor 
OBJ2 0...0 
Int constructor 
Assignment operator (0, 0) = (100, 0) 
Destructor Called 
OBJ2 100 
Destructor Called 
Destructor Called 

Итак, давайте согласовать их с линейными источниками:

int main() 
{ 
    abc obj1; 
    // Default constructor 

    std::cout << "OBJ1 " << obj1.a << "..." << obj1.b << "\n"; 
    // OBJ1 0...0 

    abc obj2; 
    // Default constructor 

    std::cout << "OBJ2 " << obj2.a << "..." << obj2.b << "\n"; 
    // OBJ2 0...0 

    obj2 = 100; 
    // Int constructor 
    // Assignment operator (0, 0) = (100, 0) 
    // Destructor Called 

    std::cout << "OBJ2 " << obj2.a << "\n"; 
    // OBJ2 100 

    return 0; 
    // Destructor Called 
    // Destructor Called 
} 

У вас в основном было все , давайте рассмотрим сюрпризы.

Первый сюрприз: хотя obj2 меняет значение позже abc obj2; все равно вызовет конструктор по умолчанию в точке объявления.

Второй сюрприз: obj2 = 100 фактически означает obj2.operator=(abc(100));, то есть:

  • Построить временную (безымянным) abc от abc(100)
  • Назначают его obj2
  • уничтожить временный, прежде чем перейти к следующему оператору

Третий сюрприз: деструкторы называются в конце o в объеме, непосредственно перед закрывающей скобкой } (и да, послеreturn). Поскольку вы используете system("pause"), я предполагаю, что вы находитесь на Windows => хотя удача, которую они вызывают после завершения паузы, и, таким образом, ваша консоль Windows исчезает в мгновение ока в тот момент, когда они появятся. Вы можете запустить программу с более постоянной консоли или использовать дополнительную область:

int main() { 
    { 
    // your code here 
    } 
    system("pause"); 
    return 0; 
} 
1

Это потому, что есть конструктор, который может принимать аргумент int типа. Этот временный созданный объект копируется в obj2 путем вызова назначения копирования по умолчанию.

Чтобы избежать таких преобразований, создайте конструктор как явный.

5
obj2 = 100; 

Вы определили конструктор, который принимает int. Это допускает неявное преобразование из int в abc. Для этого требуется создание нового объекта. Он не просто волшебным образом задает поле в существующем объекте, вызывая конструктор; конструкторы конструкция новые объекты.

EDIT: Правильная последовательность событий от @Steve Jessop

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

+4

«первый экземпляр уничтожен и создается новый экземпляр, который заменяет оригинал» - это неправильно. Создается новый экземпляр, затем копируется в оригинал, затем временно (а не оригинал) уничтожается. Назначение копии действительно волшебным образом устанавливает оба поля в существующем объекте. –

+0

@SteveJessop: Хороший звонок –

6

Но когда я задаю obj2 = 100, как компилятор позволяет инициализировать целое число в объект класса?

Это происходит потому, что, когда вы делаете следующее:

obj2 = 100; 

это один будет первым назвать abc(int x) для создания объекта класса, а затем вызвать оператор присваивания в копии по умолчанию (так как не гается определяется), чтобы присвоить значение 100 существующим obj2. После назначения временный объект разрушается.

Если вы не желаете этого эффекта, отметьте деструктор как explict, чтобы избежать неявных вызовов.

explicit abc(int x) { 
    //do something 
} 
1

Вы деструктор называется 3 раза, вы не можете видеть его из-за паузы.

+0

Вместо 'pause' я использовал« getchar() », но все же я мог видеть только один раз, когда вызов деструктора –

+1

Вы увидите их * после * нажмите клавишу. – jrok

+0

Но он их не видит, потому что его выходное окно уходит, когда он нажимает клавишу :-) –

Смежные вопросы