C++ вызов конструктора из конструктора

pitrik2

вроде как нельзя
однако в гугле нашел решение
токо терь не пойму как оно работает :(

#include <iostream>

class A {
public:
int b;
int c;

A(int a) {
b = a;
};
A {
c=4;
// this->A::A(2);
new (this)A(2);
};

};

int main {
A a;

std::cout << a.b << std::endl;
std::cout << a.c << std::endl;

return 0;
}

закомментированная строчка будет работать в вижуалсстудии
но она вроде как противоречит стандарту
The address of a constructor shall not be taken.

stat7984215

вроде как нельзя
однако в гугле нашел решение
токо терь не пойму как оно работает :(
Работает, т.к. operator new можно перегружать и существует его стандартная перегрузка void * operator new (size_t bytes, void * mem которая и используется в этом примере. Это называется placement new и используется для написания собственных распределителей памяти.
закомментированная строчка будет работать в вижуалсстудии
но она вроде как противоречит стандарту
Ну, это проблемы вижуалсстудии, если она на эту строчку даже предупреждения не выдает.
Вообще, вызывать конструктор из конструктора это очень плохая идея, т.к. черевато ошибками, например:

#include <iostream>

struct B {
B {
std::cout << "B::B" << std::endl;
p = new char[10];
}

~B {
std::cout << "B::~B" << std::endl;
delete p;
}

char * p;
};

struct D : public B {
D(int) {
std::cout << "D::D(int)" << std::endl;
}
D {
std::cout << "D::D" << std::endl;
new (this)D(1);
}

~D {
std::cout << "D::~D" << std::endl;
}
};

int main {
D d;
return 0;
}

В результате имеем утечку памяти:

B::B
D::D
B::B
D::D(int)
D::~D
B:~B

Могут возникать и более хитрые проблемы, например, если бы в твоем примере выше встретилось бы

class A;
void * operator new(size_t size, A * mem)
{
return std::memset(mem, 0, size);
}

то результат его работы был бы другим.
Так что вместо вызова одного конструктора из другого лучше написать функцию инициализации и звать ее изо всех конструкторов.

Maurog

<offtopic>
даже если бы оба варианта работали по стандарту, то тем не менее они грязные. можно сделать метод инициализации и его вызвать из обоих конструкторов (если требуется избавиться от копи-паста)
</offtopic>
не нашел в коде места, где берется адрес конструктора

smit1

Вкратце - чувак, тебе это не нужно. Подожди С++0x, там это можно будет легально делать.

pitrik2

ясн
баян значится

rosali

блин ну чё за глупости, неужели не понятно, что ты так дважды поля b и c проинициализируешь. а если их тип чуть сложнее чем int, память например аллоцируется при инициализации... :confused:

klyv

а что насчёт возможности Java, C# вызова другого конструктора?

rosali

а что насчет сборки мусора в Java, C#?

klyv

при правильном использовании, вызов конструкторов из конструкторов не помешает абсолютно ничем.
надо только, как в первом примере, не делать инициализацию вне них.

Dasar

а что насчёт возможности Java, C# вызова другого конструктора?
в C# вызывать можно, но только в самом начале через специальный синтаксис

class A
{
public A(int a){this.b=a;}
public A(int a, int c):this(a){this.c = c;}

int b;
int c;
}

Dasar

надо только, как в первом примере, не делать инициализацию вне них.
и чем это поможет примеру ?

klyv

в C# вызывать можно, но только в самом начале через специальный синтаксис
я к тому, что компилятор сам следит за выполнением конструкторов и инициализацию аккуратно обходит, потому проблем не возникает.

rosali

ну как же не возникает-то :ooo:

struct Int {
int * x;
Int {x = new int;}
~Int { delete x; }
}
class C {
Int x;
C (int z)
{
*(x.x) = z;
}
C
{
new (this) C(0);
}
}
C c; //new int случается дважды!

klyv

и чем это поможет примеру ?
ну да, этот пример показывает, что лучше применять это не в конструкторах, о которых компилятор слишком много знает, а в каких-нить init'ах, вызываемых сразу из конструкторов.

klyv

new int случается дважды!
Ты инициализацию оставил вне конструктора ;)

bleyman

Ты что именно хочешь сказать, что в твоей реализации компилятора инициализация обязана случиться дважды, или что стандарт плюсов говорит, что инициализация должна случиться дважды, или что в любом языке, допускающем вызовы других конструкторов, инициализация случится дважды и только сборщик мусора спасает положение?
Если третье, то это тупо неверно. В том же сишарпе инициализация довольно тривиально отделена от конструкторов, поэтому происходит один раз. Более того, там ещё всё веселей, порядок такой: вначале выполняются инициализаторы от most derived class к least derived (то есть к System.Object, если бы они у него были) потом выполняются конструкторы в обратном порядке (то есть вначале least derived). Сделано это затем, чтобы даже если ты вызываешь в конструкторе виртуальную функцию, она всё равно увидит инициализированные поля своего реального класса, инициализированные только инициализаторами, а не конструктором, конечно, но и то хлеб.
Я к тому, что в результате необходимость как-то дать возможность писать вместо "public MyClass(bla bla) base(bla) {}" "public MyClass(bla bla) this(bla) {}" в общем-то уже не пугает, под Кандагаром было круче.

klyv

под Кандагаром было круче
где-где-где?
Оставить комментарий
Имя или ник:
Комментарий: