PDA

Показать полную графическую версию : Порядок построения классов


pva
18-03-2009, 09:20
Где можно почитать про порядок построения классов (с виртуальными функциями)? Пример:

struct A
{
virtual void foo() {cout << "A::foo()\n";}
}

struct C
{
C(A& a) {a.foo();}
}

struct B : A, C
{
B() : A(), C(*this)
{
}

void foo() {cout << "B::foo()\n";}
}

что по правилам должно появиться на экране, A::foo(), B::foo() или какая-нить ошибка, что класс недостроен?

Alan85
18-03-2009, 19:16
virtual void foo() {cout << "A::foo()\n";} »
может я чтото уже подзабыл из классов си++ но член класса объявленый виртуальным не может иметь реализацию тут же.
и объявление класса должно быть начинатся с class <name> . Почитать можно в любой более менее нормальной книге по Си++ . Ну а про классы в своей библиотеке нашел "Павловская Т. C++ Объектно-ориентировочное программирование. Практикум"

pva
19-03-2009, 07:34
не может иметь реализацию тут же »
Почему нет? классический пример из книги страуструпа:

class shape_t
{
public:
virtual ~shape_t() {}
}

Методы, реализованные в описании раскрываются inline когда могут (если не могут, то не inline)
должно быть начинатся с class <name> »
struct - это тоже класс (любой тип данных есть класс для C++)Павловская Т. C++ Объектно-ориентировочное программирование. Практикум »
Есть ссылка на "скачать в интернете"?

Alan85
19-03-2009, 08:36
struct »
не есть истинный класс - в нем нет такой фишки как private.
Где скачать не знаю, я двд с книгами заказал - куча чего полезного и интересного.

pva
19-03-2009, 10:18
фишки как private. »
не верю:

struct A
{
private:
int _priv_data;
};

вполне собирается любым компилятором C++. Ссылку на книжку не пока могу дать
Более того, даже int есть класс, а у него даже и методов то нет...

Alan85
19-03-2009, 19:25
да, извини, согласен - давно классами в сях не баловался. А верхний пример будет откомпилирован и выведет A::foo(), B::foo() если немного подправить синтаксис (точки с запетой между реализациями). Только хоть используй virtual тут хоть нет - результат тот же.

#include <iostream>
using namespace std;
class A
{
public:
void foo() {cout << "A::foo()\n";}
} ;
class C
{
public:
C(A& a) {a.foo();}
} ;

class B : A, C
{
public:
B() : A(), C(*this){/**/}
void foo() {cout << "B::foo()\n";}
} ;
main()
{
B b;
b.foo();
cin.get();
}

Alan85
19-03-2009, 19:50
#include <iostream>
using namespace std;
class A
{
public:
virtual void foo() {cout << "A::foo()\n";}
} ;
class C
{
public:
C(A& a) {a.foo();}
} ;

class B : public A
{
public:
void foo() {cout << "B::foo()\n";}
} ;
main()
{
B* b=new B();
A *a=b;
a->foo();
b->foo();
cin.get();
}

а тут слово virtual имеет значение. Тут типичный случай полиморфизма - объект A подменяет свои виртуальные функции функциями из объекта B.

pva
20-03-2009, 22:29
Только хоть используй virtual тут хоть нет - результат тот же. »
вот пример когда есть отличия:

#include <iostream>
using namespace std;
class A
{
public:
void foo() {cout << "A::foo()\n";}
// функция test_foo не знает, какой именно класс передан аргументом.
// она просто получила некоторый интерфейс, одинаковый для всех наследников
friend test_foo(A& a) {a.foo();}
} ;
class C
{
public:
C(A& a) {a.foo();}
} ;

class B : A, C
{
public:
B() : A(), C(*this){/**/}
void foo() {cout << "B::foo()\n";}
} ;
main()
{
A a;
B b;
test_foo(a); // вызов одной функции с разными реализациями foo()
test_foo(b);
cin.get();
}

Внимание вопрос! в классе B функция foo вызывается 2 раза. Каждый раз результаты разные.
a->foo();
b->foo(); »
Испльзования класса хранилища (auto или в свободной памяти) не влияет на правила полиморфизма и построения классов, поэтому через operator new() и указатели не было смысла переделывать
A подменяет свои виртуальные функции »Это то понятно, это на случай, когда оно всё гладко. А вот рассмотрим случай использования недостроенного класса. Рассмотрим конструктор класса (пример сделан специально, чтобы достигнуть нужного эффекта)

struct B : A, C
{
// порядок вызова:
// 1. A::A()
// 2 С::C(A&) - подкласс A уже построен, но B:A,C ещё нет
// 3 тело B::B()
B() : A(), C(*this)
{
}

void foo() {cout << "B::foo()\n";}
}

Верно ли, что по правилам C++ в случае использования недостроенного класса с виртуальными функциями будут использоваться реализации уже достроенных подклассов либо, в случае использования чистых виртуальных функций, выводится сообщение об ошибке на усмотрение компилятора (например обращение по адресу 0x00000000)
~либо~
Верно ли что использовать недостроенные классы категорически нельзя, реализация поведения остаётся на усмотрение компилятора.
Я ни того, ни другого утверждения ещё не встречал

Alan85
20-03-2009, 23:25
в случае использования недостроенного класса »
наверно ты имеешь ввиду абстрактный класс.

class A //абстрактный
{
public:
virtual void foo()=0;
} ;

То тогда вылетит со свистом так как в в конструкторе C(A& a) {a.foo();} передается B которое еще не создано (после C будет ) что значит B::foot() еще неизвестно (не построена таблица виртуальных функций).
Но стоит только заменить класс A обратно с реализацией функции (она сразу вносится в таблицу виртуальных функций) все отлично - C(A& a) {a.foo();} хоть и принимает B но в таблице не найдя для нее реализации B::foo() передает выше - A::foo().
Я не утверждаю что это истина и верно на всех компиляторах хотя логически должно.

pva
22-03-2009, 17:44
наверно ты имеешь ввиду абстрактный класс. »
Я имел ввиду именно НЕДОСТРОЕННЫЙ класс С РЕЛИЗАЦИЕЙ (когда непонятно, как должна себя вести софтина)

Alan85
22-03-2009, 18:03
Тогда я чтото не пойму :

struct A
{
virtual void foo() {cout << "A::foo()\n";}
}

struct C
{
C(A& a) {a.foo();}
}

struct B : A, C
{
B() : A(), C(*this)
{
}

void foo() {cout << "B::foo()\n";}
}

По каким критериям он не достроен и одновременно с реализацией. По-моему вполне достроен и реализован.

pva
23-03-2009, 07:37
В конструктор C передаётся часть недостроенного класса B с собственной реализацией виртуальных функций (*this)

Alan85
23-03-2009, 07:50
В конструкторе передается не сам не достроенный объект а его указатель типа B. В самое классе C он преобразуется в класс A (он же подкласс его) что дает a.foo() корректно. Вот вопрос что будет если сделать так C(B& b) {b.foo();} - надо будет дома проверить :)

pva
23-03-2009, 14:36
если сделать так C(B& b) {b.foo();} »
С этим как раз понятно, в лучшем случае слетит ибо используем неинициализированную память с мусором.
(он же подкласс его) что дает a.foo() корректно »
a.foo() - виртуальным метод, имеющий реализации A::foo() и B::foo(). Например:

struct A
{
virtual void foo() {cout << "A::foo()\n";}
};

struct C
{
C(A& a) {test(a);}

void test(A& a)
{
clog << "C::test with &a=" << &a << ", this=" << this << "\n";
a.foo();
}
};

struct B : A, C
{
B() : A(), C(*this)
{
test(*this);
}

void foo() {cout << "B::foo()\n";}
};

С одними и теми же исходными данными получаем разный результат. Потому что первый раз вызов C::test произойдёт когда формально B ещё не достроен

C::test with &a=1245060, this=1245064
A::foo()
C::test with &a=1245060, this=1245064
B::foo()

Это особенность компилятора или стандарт с++ такой?




© OSzone.net 2001-2012