Вызов конструктора
От: Atilla Россия  
Дата: 25.12.02 12:32
Оценка:
Корректен ли будет такой

struct A
{
A(int x) {}
A(int x, int y)
{
  A(x);
}
};

A a(1, 2);


вызов конструктора из другого конструктора с точки зрения стандарта или нет?
Что тут произойдет, вызовется метод текущего объекта или будет создан временный объект?
Кр-ть — с.т.
Re: Вызов конструктора
От: kmn Украина  
Дата: 25.12.02 12:34
Оценка:
Здравствуйте, Atilla, Вы писали:

A>Корректен ли будет такой


A>
A>struct A
A>{
A>A(int x) {}
A>A(int x, int y)
A>{
A>  A(x);
A>}
A>};

A>A a(1, 2);
A>


A>вызов конструктора из другого конструктора с точки зрения стандарта или нет?

A>Что тут произойдет, вызовется метод текущего объекта или будет создан временный объект?

будет создан временный объект
Re: Вызов конструктора
От: ssm Россия  
Дата: 25.12.02 12:35
Оценка:
Здравствуйте, Atilla, Вы писали:

A>вызов конструктора из другого конструктора с точки зрения стандарта или нет?

A>Что тут произойдет, вызовется метод текущего объекта или будет создан временный объект?

второе
Re: Вызов конструктора
От: Lexey Россия  
Дата: 25.12.02 12:37
Оценка:
Здравствуйте, Atilla, Вы писали:

A>
A>struct A
A>{
A>A(int x) {}
A>A(int x, int y)
A>{
A>  A(x);
A>}
A>};

A>A a(1, 2);
A>


A>вызов конструктора из другого конструктора с точки зрения стандарта или нет?


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

A>Что тут произойдет, вызовется метод текущего объекта или будет создан временный объект?


Если компилятор позволяет такие вещи, то, по идее, это должен быть обычный вызов метода.
"Будь достоин победы" (c) 8th Wizard's rule.
Re: Вызов конструктора
От: Павел Кузнецов  
Дата: 25.12.02 12:38
Оценка:
Здравствуйте, Atilla, Вы писали:

A>Корректен ли будет такой


A>
A>struct A
A>{
A>A(int x) {}
A>A(int x, int y)
A>{
A>  A(x);
A>}
A>};

A>A a(1, 2);
A>


A>вызов конструктора из другого конструктора с точки зрения стандарта или нет?


Для данного определения класса A — нет.

A>Что тут произойдет, вызовется метод текущего объекта или будет создан временный объект?


Если бы в классе A был определен конструктор по умолчанию, была бы определена переменная x, и соответственно, вызван конструктор A::A(). Строка

A(x);


эквивалентна строке:

A x;
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[2]: Вызов конструктора
От: Anton V. Kolotaev  
Дата: 25.12.02 12:42
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:


ПК>Если бы в классе A был определен конструктор по умолчанию, была бы определена переменная x, и соответственно, вызван конструктор A::A().


А не будет ли создаваться временный объект, для него отрабатывать A::A(int) с аргументом x, и тут же уничтожаться???
... << RSDN@Home 1.0 beta 3 >>
Re[2]: Вызов конструктора
От: Павел Кузнецов  
Дата: 25.12.02 12:44
Оценка:
Здравствуйте, kmn, Вы писали:

A>>
A>>struct A
A>>{
A>>A(int x) {}
A>>A(int x, int y)
A>>{
A>>  A(x);


kmn>будет создан временный объект


А должна быть ошибка компиляции, т.к. 1) переменная x уже определена как формальный параметр конструктора 2) в классе A нет конструктора по умолчанию, необходимого для инициализации определяемой переменной с именем х.

Comeau C/C++ 4.3.0.1 (Aug 21 2002 15:45:32) for MS_WINDOWS_x86
Copyright 1988-2002 Comeau Computing.  All rights reserved.
MODE:strict warnings C++

"test.cpp", line 6: error: "x" has already been declared in the current scope
    A(x);
      ^

"test.cpp", line 6: error: no default constructor exists for class "A"
    A(x);
      ^
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re: Вызов конструктора (вдогонку)
От: Atilla Россия  
Дата: 25.12.02 12:44
Оценка:
A>Корректен ли будет такой

...

A>вызов конструктора из другого конструктора с точки зрения стандарта или нет?

A>Что тут произойдет, вызовется метод текущего объекта или будет создан временный объект?

Желательно чтобы ответ включал ссылку на пункт стандарта.
Кр-ть — с.т.
Re: Вызов конструктора
От: Lorenzo_LAMAS  
Дата: 25.12.02 12:54
Оценка:
Ну, может написать this->A::A(x);
Of course, the code must be complete enough to compile and link.
Re[2]: Вызов конструктора (вдогонку)
От: Павел Кузнецов  
Дата: 25.12.02 13:18
Оценка: 38 (4)
Здравствуйте, Atilla, Вы писали:

A>>Корректен ли будет такой


A>...


A>>вызов конструктора из другого конструктора с точки зрения стандарта или нет?

A>>Что тут произойдет, вызовется метод текущего объекта или будет создан временный объект?

A>Желательно чтобы ответ включал ссылку на пункт стандарта. :)


Если A — имя структуры, то

A(x);


Является объявлением переменной x типа A.

  • A является decl-specifier-seq:
    A — class-name.
    class-name — type-name (7.1.5.2/1)
    type-name — simple-type-specifier (7.1.5.2/1)
    simple-type-specifier — type-specifier (7.1.5/1)
    type-specifier — decl-specifier (7.1/1)
    decl-specifier — decl-specifier-seq (7.1/1)
  • (x) является init-declarator-list
    x — identifier
    identifier — id-expression (5.1/1)
    unqualified-id — id-expression (5.1/1)
    id-expression — declarator-id (8/4)
    declarator-id — declarator (8/4)
    ( declarator ) — direct-declarator (8/4)
    direct-declarator — declarator (8/4)
    declarator — init-declarator (8/1)
    init-declarator — init-declarator-list (8/1)
  • Любое предложение языка C++ вида "decl-specifier-seq init-declarator-list ;" является объявлением (7/1), в данном случае объявлением переменной x типа A.
  • В случае, если какое-то предложение может быть как объявлением, так и выражением, оно является объявлением (6.8).
  • Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
    Re[3]: Вызов конструктора
    От: Павел Кузнецов  
    Дата: 25.12.02 13:19
    Оценка:
    Здравствуйте, Anton V. Kolotaev, Вы писали:

    ПК>>Если бы в классе A был определен конструктор по умолчанию, была бы определена переменная x, и соответственно, вызван конструктор A::A().


    AV>А не будет ли создаваться временный объект, для него отрабатывать A::A(int) с аргументом x, и тут же уничтожаться???


    Нет, подробнее см. мой ответ Atill'e.
    Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
    Re[3]: Вызов конструктора (вдогонку)
    От: Atilla Россия  
    Дата: 25.12.02 13:26
    Оценка:
    Спасибо за столь подробный ответ!

    А вот такое:


    A(int x. int y)
    {
    this->A::A(x);
    }


    будет корректным вызовом конструктора?
    Кр-ть — с.т.
    Re[4]: Вызов конструктора (вдогонку)
    От: __Nicolay Россия  
    Дата: 25.12.02 13:36
    Оценка:
    Здравствуйте, Atilla, Вы писали:

    А placement new не подойдет?

    A(int x. int y)
    {
        new (this) A(x);
    }
    ... << RSDN@Home 1.0 beta 1 >>
    Re[4]: Вызов конструктора (вдогонку)
    От: Павел Кузнецов  
    Дата: 25.12.02 14:11
    Оценка:
    Здравствуйте, Atilla, Вы писали:

    A>А вот такое:


    A>

    A>
    A>A(int x. int y)
    A>{
    A>  this->A::A(x);
    A>}
    A>


    A>будет корректным вызовом конструктора?


    Нет. Т.к. у конструктора нет имени, его нельзя вызвать непосредственно.
    Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
    Re[5]: Вызов конструктора (вдогонку)
    От: Павел Кузнецов  
    Дата: 25.12.02 14:14
    Оценка:
    Здравствуйте, __Nicolay, Вы писали:

    N>А placement new не подойдет?


    N>
    N>A(int x. int y)
    N>{
    N>    new (this) A(x);
    N>}
    N>


    Не вполне. Создавать объект можно только на "чистом" месте.

    A::A(int x, int y)
    {
      this->~A();      // явный вызов деструктора
      new (this) A(x); // создание на этом же месте объекта A
    }


    Но и это чревато, если A создается как базовый подобъект другого класса, скажем B. В частности, в объекте вместо vmt B будет записан указатель на vmt A.
    Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
    Re[6]: Вызов конструктора (вдогонку)
    От: __Nicolay Россия  
    Дата: 26.12.02 07:39
    Оценка:
    Здравствуйте, Павел Кузнецов, Вы писали:

    ПК>Не вполне. Создавать объект можно только на "чистом" месте.

    А разве в момент вызова конструктора это еще не чистое место?
    Если нет, то какие же действия предшествуют?

    ПК>
    ПК>A::A(int x, int y)
    ПК>{
    ПК>  this->~A();      // явный вызов деструктора
    ПК>  new (this) A(x); // создание на этом же месте объекта A
    ПК>}


    ПК>Но и это чревато, если A создается как базовый подобъект другого класса, скажем B. В частности, в объекте вместо vmt B будет записан указатель на vmt A.



    А не опасно ли вызывать деструктор для недоконструированного объекта?

    Если я правильно вас понял вы хотите сказать (не совсем понял словосочетание базовый подобъект) что в таком коде:
    class A
    {
    public:
        A() { new (this) A(0); }
        A(int i_):i(i_) {}
    
        virtual ~A() {}
    
    private:
        int i;
    };
    
    class B : public A
    {
    public:
        B():A() {}
        B(int i_):A(i_) {}
    
        virtual ~B() {}
    };
    
    int main()
    {
        B b;
    
        return 0;
    }


    В b будет vmt от класса A, я проверял этот код в VC6 и VC7 в отладчике видно что с vmt все OK.
    Даже попробовал в gcc version 2.95.3-5 (mingw special) — тоже нормально.
    ... << RSDN@Home 1.0 beta 1 >>
    Re: Вызов конструктора
    От: Кодт Россия  
    Дата: 26.12.02 13:20
    Оценка:
    Здравствуйте, Atilla, Вы писали:

    <>

    Вероятно, тебе нужна общая функция инициализации...
    Поскольку в теле конструктора ( {...} ) все предки и члены уже созданы с параметрами, указанными в списке инициализации ( A::A(int x, int y) : Base(x), m_y(y) {...} ),
    то тебе остается лишь поколдовать с вызовом методов и присваиванием.

    Поэтому
    struct A
    {
    private:
      void init(int x);
    
    public:
      A(iny x) { init(x); }
      A(int x, int y) { init(x); ... }
      ...
    };


    Или же, если нужно вызывать именно конструкторы подобъектов, сделать надстройку:
    class ABase
    {
    protected:
      ABase(int x) { ... }
    public:
      ...
    };
    
    class A : public ABase
    {
    public:
      A(int x) : ABase(x) { ... }
      A(int x, int y) : ABase(x) { ... }
    };
    Перекуём баги на фичи!
    Re[7]: Вызов конструктора (вдогонку)
    От: __Nicolay Россия  
    Дата: 27.12.02 14:45
    Оценка:
    Здравствуйте, Павел Кузнецов,
    а не могли бы вы все же ответить когда будет время,
    интересно всетаки что говорится про это в стандарте.
    ... << RSDN@Home 1.0 beta 1 >>
    Re[7]: Вызов конструктора (вдогонку)
    От: Павел Кузнецов  
    Дата: 28.12.02 10:41
    Оценка:
    Здравствуйте, __Nicolay, Вы писали:

    ПК>>Не вполне. Создавать объект можно только на "чистом" месте.

    N>А разве в момент вызова конструктора это еще не чистое место?
    N>Если нет, то какие же действия предшествуют?

    Конструирование базовых подобъектов и членов класса.

    N>А не опасно ли вызывать деструктор для недоконструированного объекта?


    Опасно. Но так же точно опасно и сконструировать базовые подобъекты и/или члены класса и не вызвать деструкторы для них, а вместо этого повторно сконструировать новые объекты поверх старых.

    N>Если я правильно вас понял вы хотите сказать (не совсем понял словосочетание базовый подобъект)


    Базовый подобъект — часть полного объекта, взятая из базового класса.

    N>что в таком коде: <...>

    N>В b будет vmt от класса A, я проверял этот код в VC6 и VC7 в отладчике видно что с vmt все OK.
    N>Даже попробовал в gcc version 2.95.3-5 (mingw special) — тоже нормально.

    Будет undefined behavior, т.к. new (this) A в конструкторе A в конечном итоге приведет к тому, что к объекту типа A будет обращение через указатель типа B*. Указатель на "чужую" vmt — один из вариантов. Например, следующая программа на VC++7.0 приводит "вылету" программы, если строка [*] закомментирована и к бесконечному вызову деструктора ~A, если ее раскомментировать:

    #include <iostream>
    
    class A
    {
    public:
      A()
      {
        //this->~A(); // [[*]
        new (this) A();
      }
    
      virtual ~A() { std::cout << "~A" << std::endl; }
    };
    
    class B : public A
    {
    public:
      B() { }
    
      virtual ~B() { std::cout << "~B" << std::endl; }
    };
    
    int main()
    {
      B* b = new B;
      delete b;
    }
    Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
    Re[8]: Вызов конструктора (вдогонку)
    От: __Nicolay Россия  
    Дата: 30.12.02 07:06
    Оценка: 9 (1)
    Здравствуйте, Павел Кузнецов, Вы писали:

    ПК>Опасно. Но так же точно опасно и сконструировать базовые подобъекты и/или члены класса и не вызвать деструкторы для них, а вместо этого повторно сконструировать новые объекты поверх старых.


    ПК>Будет undefined behavior, т.к. new (this) A в конструкторе A в конечном итоге приведет к тому, что к объекту типа A будет обращение через указатель типа B*. Указатель на "чужую" vmt — один из вариантов. Например, следующая программа на VC++7.0 приводит "вылету" программы, если строка [*] закомментирована и к бесконечному вызову деструктора ~A, если ее раскомментировать:


    Не совсем понятно почему и где должен появится указатель на чужую vmt, я вижу только один способ достичь описанного результата — это вызвать placement new (this) указав конструктор базового класса из функции члена (перед этим конечно же должен быть явный вызов деструктора), но я ведь этого и не предлагал.
    Приведенный вами код вылетает по причине переполнения стека, т.к. это просто бесконечная рекурсия, это все равно что написать так:

    
    class A
    {
    public:
      A()
      {
        //this->~A(); // [ * ]
        //new (this) A();
        f();
      }
      void f()
      {
          f();
      }
    
      virtual ~A() { std::cout << "~A" << std::endl; }
    };
    
    class B : public A
    {
    public:
      B() { }
    
      virtual ~B() { std::cout << "~B" << std::endl; }
    };
    
    int main()
    {
      B* b = new B;
      delete b;
    }



    Если же у класса, для которого хочется явно вызвать конструктор есть базовый, то в этом случае перед вызовом placement new (this), нужно явно вызвать деструктор базового класса, тогда все будет хорошо см. пример:

    #include <iostream>
    #include <new>
    
    class A
    {
    public:
        A() {std::cout<<"A::A()"<<std::endl;}
    
        virtual ~A() {std::cout<<"A::~A()"<<std::endl;}
    };
    
    class B : public A
    {
    public:
        B() 
        {
            this->A::~A();
            std::cout<<"B::B()"<<std::endl;
            new (this) B(0);
        }
    
        B(int i_):i(i_) {std::cout<<"B::B(int)"<<std::endl;}
    
        virtual ~B() {std::cout<<"B::~B()"<<std::endl;}
    
    private:
        int i;
    };
    
    class C : public B
    {
    public:
        C():B() {std::cout<<"C::C()"<<std::endl;}
        C(int i_):B(i_) {std::cout<<"C::C(int)"<<std::endl;}
    
        virtual ~C() {std::cout<<"C::~C()"<<std::endl;}
    };
    
    int main()
    {
        C* pc = new C();
        delete pc;
    
        return 0;
    }
    ... << RSDN@Home 1.0 beta 1 >>
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.