LINUX.ORG.RU

[C++] [компиляторы] Множественное наследование

 


0

1
#include <iostream>
using namespace std;

class MyBase1
{
public:
    MyBase1() {
        cout << "Con1\t" << this << endl;
    }
    MyBase1(const MyBase1 &other) {
        cout << "CCon1\t" << this << endl;
    }
    ~MyBase1() {
        cout << "Des1\t" << this << endl;
    }
    
};

class MyBase2
{
public:
    MyBase2() {
        cout << "Con2\t" << this <<  endl;
    }
    MyBase2(const MyBase2 &other) {
        cout << "CCon2\t" << this << endl;
    }
    ~MyBase2() {
        cout << "Des2\t" << this << endl;
    }
};

class MyBase12 : public MyBase1, MyBase2
{
public:
    MyBase12() {
        cout << "Con12\t" << this <<  endl;
    }
    MyBase12(const MyBase12 &other) {
        cout << "CCon12\t" << this << endl;
    }
    ~MyBase12() {
        cout << "Des12\t" << this << endl;
    }
};

class MySuperClass : public MyBase12
{
public:
    MySuperClass() {
        cout << "ConS\t" << this << endl;
    }
    MySuperClass(const MySuperClass &other) {
        cout << "CConS\t" << this << endl;
    }
    ~MySuperClass() {
        cout << "DesS\t" << this << endl;
    }
};

void func(MyBase12 r)
{
    cout << "COPY:\t" << &r <<  endl;
}

int main()
{
    const char *delim = "*****************\n";
    MySuperClass o;
    cout << "ORIG:\t" << &o << endl << delim;
    func(o);
    cout << delim;
}

gcc версия 4.5.2 20110127 (prerelease) (GCC) x86_64

Con1    0x7fff30141e66
Con2    0x7fff30141e66
Con12   0x7fff30141e66
ConS    0x7fff30141e66
ORIG:   0x7fff30141e66
*****************
Con1    0x7fff30141e67
Con2    0x7fff30141e67
CCon12  0x7fff30141e67
COPY:   0x7fff30141e67
Des12   0x7fff30141e67
Des2    0x7fff30141e67
Des1    0x7fff30141e67
*****************
DesS    0x7fff30141e66
Des12   0x7fff30141e66
Des2    0x7fff30141e66
Des1    0x7fff30141e66
Тут всё ожидаемо.

MSVC++ 2010 Express

Con1    0012FF4B
Con2    0012FF4C
Con12   0012FF4B
ConS    0012FF4B
ORIG:   0012FF4B
*****************
Con1    0012FE60
Con2    0012FE61
CCon12  0012FE60
COPY:   0012FE60
Des12   0012FE60
Des2    0012FE61
Des1    0012FE60
*****************
DesS    0012FF4B
Des12   0012FF4B
Des2    0012FF4C
Des1    0012FF4B
А тут обратите внимание на адреса объектов второго класса. Почему так?

★★★★★

> Почему так?

Деталь реализации. Разве стандарт обещает что-либо конкретное на эту тему?

Manhunt ★★★★★
()
Ответ на: комментарий от anonymous

Сам ты упоротый, msvs все правильно делает, просто видимо добавляет в класс какие-то скрытые поля, которые делают его размер ненулевым. Если в каждый класс добавить хотя бы одно поле, результат в gcc будет таким же.

anonymous
()
Ответ на: комментарий от anonymous

> m$vc - упоротый недокомпилятор

угу, это еще не затронута тема указателей на методы, вот там «гениальные» разработчики из микрософта развернулись по полной, наплодив черти-что на ровном месте, если у ТС есть желание почитать - вот статья, где это описано:

http://www.codeproject.com/KB/cpp/FastDelegate.aspx

aho
()
Ответ на: комментарий от anonymous
#include <iostream>
using namespace std;

class MyBase1
{
public:
    int x1;
    MyBase1() {
        cout << "Con1\t" << this << endl;
    }
    MyBase1(const MyBase1 &other) {
        cout << "CCon1\t" << this << endl;
    }
    ~MyBase1() {
        cout << "Des1\t" << this << endl;
    }
    
};

class MyBase2
{
public:
    int x2;
    MyBase2() {
        cout << "Con2\t" << this <<  endl;
    }
    MyBase2(const MyBase2 &other) {
        cout << "CCon2\t" << this << endl;
    }
    ~MyBase2() {
        cout << "Des2\t" << this << endl;
    }
};

class MyBase12 : public MyBase1, MyBase2
{
public:
    int x12;
    MyBase12() {
        cout << "Con12\t" << this <<  endl;
    }
    MyBase12(const MyBase12 &other) {
        cout << "CCon12\t" << this << endl;
    }
    ~MyBase12() {
        cout << "Des12\t" << this << endl;
    }
};

class MySuperClass : public MyBase12
{
public:
    int xs;
    MySuperClass() {
        cout << "ConS\t" << this << endl;
    }
    MySuperClass(const MySuperClass &other) {
        cout << "CConS\t" << this << endl;
    }
    ~MySuperClass() {
        cout << "DesS\t" << this << endl;
    }
};

void func(MyBase12 r)
{
    cout << "COPY:\t" << &r <<  endl;
}

int main()
{
    const char *delim = "*****************\n";
    MySuperClass o;
    cout << "ORIG:\t" << &o << endl << delim;
    func(o);
    cout << delim;
}

Как ты и предвещал.

Con1    0x7fff87579f20
Con2    0x7fff87579f24
Con12   0x7fff87579f20
ConS    0x7fff87579f20
ORIG:   0x7fff87579f20
*****************
Con1    0x7fff87579f30
Con2    0x7fff87579f34
CCon12  0x7fff87579f30
COPY:   0x7fff87579f30
Des12   0x7fff87579f30
Des2    0x7fff87579f34
Des1    0x7fff87579f30
*****************
DesS    0x7fff87579f20
Des12   0x7fff87579f20
Des2    0x7fff87579f24
Des1    0x7fff87579f20

Obey-Kun ★★★★★
() автор топика

Зачем ты превращаешь Development в свою жжшечку, где люди зачем-то должны смотреть, как ты изучаешь С++. Не мог бы ты прекратить это?

unanimous ★★★★★
()
Ответ на: комментарий от Boy_from_Jungle

>На llvmе-е смотрел?

clang не добавляет лишних полей в класс.

Почему так?

Непонятно почему у объекта B другой адрес? Потому что класс C можно представить примерно так:

[code] class C { A parent1; B parent2; // Поля объекта C. }; [/code]

anonymous
()
Ответ на: комментарий от anonymous

На llvmе-е смотрел?

clang не добавляет лишних полей в класс.

Почему так?

Непонятно почему у объекта B другой адрес? Потому что класс C можно представить примерно так:

 
class C
{ 
    A parent1;
    B parent2;
    // Поля объекта C.
}; 
anonymous
()
Ответ на: комментарий от Boy_from_Jungle

> Тут уже была тема когда-то похожая, m$vc - сливает

Кому и куда он сливает? Если можно, с пруфлинками.

Manhunt ★★★★★
()
Ответ на: комментарий от unanimous

> Зачем ты превращаешь Development в свою жжшечку, где люди зачем-то должны смотреть, как ты изучаешь С++. Не мог бы ты прекратить это?

Он довольно любопытные вопросы поднимает. Пока жж-шечка строго по теме, пусть будет.

Manhunt ★★★★★
()
Ответ на: комментарий от unanimous

Именно этот вопрос очень полезен, может кто увидит и поймет почему C-style кастинг и преобразование к void* - плохо.

anonymous
()

ВНЕЗАПНО. Полюбуйтесь. Убрал явный копирующий конструктор.

#include <iostream>
using namespace std;

class MyBase1
{
public:
    MyBase1() {
        cout << "Con1\t" << this << endl;
    }
    ~MyBase1() {
        cout << "Des1\t" << this << endl;
    }
    
};

class MyBase2
{
public:
    MyBase2() {
        cout << "Con2\t" << this <<  endl;
    }
    ~MyBase2() {
        cout << "Des2\t" << this << endl;
    }
};

class MyBase12 : public MyBase1, MyBase2
{
public:
    MyBase12() {
        cout << "Con12\t" << this <<  endl;
    }
    ~MyBase12() {
        cout << "Des12\t" << this << endl;
    }
};

class MySuperClass : public MyBase12
{
public:
    MySuperClass() {
        cout << "ConS\t" << this << endl;
    }
    ~MySuperClass() {
        cout << "DesS\t" << this << endl;
    }
};

void func(MyBase12 r)
{
    cout << "COPY:\t" << &r <<  endl;
}

int main()
{
    const char *delim = "*****************\n";
    MySuperClass o;
    cout << "ORIG:\t" << &o << endl << delim;
    func(o);
    cout << delim;
}

gcc версия 4.5.2 20110127 (prerelease) (GCC) x86_64

Con1    0x7fffe7472e06
Con2    0x7fffe7472e06
Con12   0x7fffe7472e06
ConS    0x7fffe7472e06
ORIG:   0x7fffe7472e06
*****************
COPY:   0x7fffe7472e07
Des12   0x7fffe7472e07
Des2    0x7fffe7472e07
Des1    0x7fffe7472e07
*****************
DesS    0x7fffe7472e06
Des12   0x7fffe7472e06
Des2    0x7fffe7472e06
Des1    0x7fffe7472e06

И гвоздь нашей программы, MS студия:

Con1    0012FF4B
Con2    0012FF4C
Con12   0012FF4B
ConS    0012FF4B
ORIG:   0012FF4B
*****************
COPY:   0012FE4C
Des12   0012FE4C
Des2    0012FE4D
Des1    0012FE4C
Des12   0012FE67
Des2    0012FE68
Des1    0012FE67
*****************
DesS    0012FF4B
Des12   0012FF4B
Des2    0012FF4C
Des1    0012FF4B
Посчитайте деструкторы. WTF?

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от aho

Это не я собираю. Сейчас спрошу. У меня только gcc.

Кстати, второе для MSVS 2008 Professional Edition.

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от unanimous

зря вы так. может, ктонить не шибко шарящий в с++, например я, узнает нечто новое?

TERRANZ ★★★★
()
Ответ на: комментарий от unanimous

> Зачем ты превращаешь Development в свою жжшечку, где люди зачем-то должны смотреть, как ты изучаешь С++. Не мог бы ты прекратить это?

Я сам C++ особо не изучаю. Мне моих знаний хватает. Сиё на практике никогда не пригодится, просто случайно напоролся и любопытство загрызло.

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от aho

> дебажную версию собираешь?

В обоих одинаково.

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от nanoo_linux

WTF. Если деструктор сделать виртуальным хотя бы в одном из базовых классов, то в студии всё ок.

Obey-Kun ★★★★★
() автор топика

IMHO примерно так.

Для любого не-POD типа компилятор вправе добавить скрытые поля.

К этому добавляется дискуссия (не эта, а вообще) о различии экземпляров по адресам. В частности, многие гуру/маньяки считают что при приведении типа к одному из базовых его адрес должен отличаться.

Вот похоже что MS и следует этому пожеланию.

ly
()
Ответ на: комментарий от ly

>В частности, многие гуру/маньяки считают что при приведении типа к одному из базовых его адрес должен отличаться.

Он не может не отличатся. В gcc тоже отличается, если посмотреть внимательно.

zombiegrinder_6000
()

Да, конструкция чтобы найти начало объекта в памяти при множественном наследовании выглядит так:

dynamic_cast<void*>(objPtr);

Очевидно чтобы работал dynamic_cast нужна поддержка RTTI, а RTTI наботает только с полиморфными типами, имеющими виртуальные функции.

Absurd ★★★
()
Ответ на: комментарий от zombiegrinder_6000

zombiegrinder_6000

Он не может не отличатся. В gcc тоже отличается, если посмотреть внимательно.

Нет, имело в виду если вот так:

class A { A(); };
class B : public A { B(); };
class C : public B { C(); };

...
C c;

printf("%p\n", &c);
printf("%p\n", static_cast<B*>(&c));
printf("%p\n", static_cast<A*>(&c));

...

7fffe7472e06
7fffe7472e05
7fffe7472e04

ly
()
Ответ на: комментарий от ly

> Для любого не-POD типа компилятор вправе добавить скрытые поля.

А можно полюбопытствовать, что компилятор в них собирается держать? Вот была у меня не-вирутальная структура, добавил я в нее конструктор копирования/деструктор и ее раздуло?

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

Это типа что бы поведение было аналогичном тому, когда что то лежит? А если из приведенных в первом примере классов собрать массив, что тогда получится?;-))))

AIv ★★★★★
()

А что говорит sizeof?

И что будет, если навернуть еще один уровень наследования - на нем еще один байт набежит?

AIv ★★★★★
()
Ответ на: комментарий от AIv

> А можно полюбопытствовать, что компилятор в них собирается держать? Вот была у меня не-вирутальная структура, добавил я в нее конструктор копирования/деструктор и ее раздуло?

Да ничего не собирается держать, но имеет право. Поэтому инициализация memset(this, 0, sizeof(*this)) может сильно гадить... Поэтому и массивы могут раздуваться.

При наличии virtual это никого её удивляет, но по правилам «забеременеть» может любой не-POD тип.

gcc тут достаточно вменяемый, упрямо следует минимализму ;-)

ly
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.