Здравствуйте, 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).
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Опасно. Но так же точно опасно и сконструировать базовые подобъекты и/или члены класса и не вызвать деструкторы для них, а вместо этого повторно сконструировать новые объекты поверх старых.
ПК>Будет 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;
}
struct A
{
A(int x) {}
A(int x, int y)
{
A(x);
}
};
A a(1, 2);
вызов конструктора из другого конструктора с точки зрения стандарта или нет?
Что тут произойдет, вызовется метод текущего объекта или будет создан временный объект?
Здравствуйте, 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>Что тут произойдет, вызовется метод текущего объекта или будет создан временный объект?
Здравствуйте, 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>>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);
^
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
...
A>вызов конструктора из другого конструктора с точки зрения стандарта или нет? A>Что тут произойдет, вызовется метод текущего объекта или будет создан временный объект?
Желательно чтобы ответ включал ссылку на пункт стандарта.
Здравствуйте, Anton V. Kolotaev, Вы писали:
ПК>>Если бы в классе A был определен конструктор по умолчанию, была бы определена переменная x, и соответственно, вызван конструктор A::A().
AV>А не будет ли создаваться временный объект, для него отрабатывать A::A(int) с аргументом x, и тут же уничтожаться???
Нет, подробнее см. мой ответ Atill'e.
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, __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.
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Не вполне. Создавать объект можно только на "чистом" месте.
А разве в момент вызова конструктора это еще не чистое место?
Если нет, то какие же действия предшествуют?
ПК>
ПК>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) — тоже нормально.
Вероятно, тебе нужна общая функция инициализации...
Поскольку в теле конструктора ( {...} ) все предки и члены уже созданы с параметрами, указанными в списке инициализации ( A::A(int x, int y) : Base(x), m_y(y) {...} ),
то тебе остается лишь поколдовать с вызовом методов и присваиванием.
Здравствуйте, __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;
}
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, __Nicolay, Вы писали:
ПК>>Будет undefined behavior, т.к. new (this) A в конструкторе A в конечном итоге приведет к тому, что к объекту типа A будет обращение через указатель типа B*. Указатель на "чужую" vmt — один из вариантов.
N>Не совсем понятно почему и где должен появится указатель на чужую vmt,
Не должен, а может :-)
N>я вижу только один способ достичь описанного результата
Наличие неопределенного поведения предполагает миллион таких способов.
ПК>>Например, следующая программа <...>
N>Приведенный вами код вылетает по причине переполнения стека, т.к. это просто бесконечная рекурсия
Семен Семеныч... :-)
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Наличие неопределенного поведения предполагает миллион таких способов.
Я просто не до конца убежден что это undefined behavior. Это наверно следует из пунктов стандарта 3.8.5 — 3.8.7, только вот откуда конкретно, там слишком много хитрых терминов которыми я пока не владею (вроде нетривиальный конструктор/деструктор)?
Неопределено поведение только для классов с vmt или всегда?
Тогда код наподобие того что приведен в 3.8.7 для классов с виртуальными методами тоже приведет к undefined behavior?
N>>Приведенный вами код вылетает по причине переполнения стека, т.к. это просто бесконечная рекурсия
ПК>Семен Семеныч...
Я не прав?
Павел, я конечно понимаю, что это старый топик, но раз уж он всплыл
Мне что-то твой пример совсем не понравился. Уж если писать, то так
class A{
public:
A()
{
std::cout<<"A def ctor\n";
}
A(int)
{
this->A::~A();
new(this)A;
}
virtual ~A()
{
std::cout<<"A dtor\n";
}
};
class B:public A{
public:
B():A(10)
{}
virtual ~B()
{
std::cout<<"B def ctor\n";
}
};
int main()
{
B * pB = new B;
delete pB;
return 0;
}
Но, хотя это и злостный undefined, все равно деструктор вызовется правильный и сработает все как и ожидалось, потому как ведь конструктор B все равно правильно установит vptr.
Так что пример неудачен.
Of course, the code must be complete enough to compile and link.
Здравствуйте, Lorenzo_LAMAS, Вы писали:
LL>Но, хотя это и злостный undefined, все равно деструктор вызовется правильный и сработает все как и ожидалось, потому как ведь конструктор B все равно правильно установит vptr.
Ну а если делать как в моем примере могут быть проблемы или нет?
Microsoft вообще вызываят конструкторы когда хочет, даже инициализирует константные члены заново например: