Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Общие вопросы C/C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 13.11.2009, 22:30   #1
MasterGH
Пользователь
 
Аватар для MasterGH
 
Регистрация: 08.11.2009
Сообщений: 16
По умолчанию ООП. Обращение к вирт. функциями в вирт. классах

Привет.
Приобретаю навыки ООП С++ и встала такая ошибка - привидение показывает не правильный результат (см. ниже)

Листинг 1:
Код:
#include <stdio.h>

class Vabstract{
	public:
	int x1; int x2;
	virtual void set(int a, int b){x1=a;x2=b;}
	virtual void getInfo(){printf("x1= %d\n",x1);printf("x2= %d\n",x2);}
};

class A: virtual public Vabstract{
	public:
	int a1; int a2;
	virtual void set(int a, int b){a1=a;a2=b;}
	virtual void getInfo(){printf("a1= %d\n",a1);printf("a2= %d\n",a2);}
};

class B: virtual public Vabstract{
	public:
	int b1; int b2;
	virtual void set(int a, int b){b1=a;b2=b;}
	virtual void getInfo(){printf("b1= %d\n",b1);printf("b2= %d\n",b2);}
};

class C: public A, public B {
	public:
	int c1; int c2;
	virtual void set(int a, int b){c1=a;c2=b;}
	virtual void getInfo(){printf("c1= %d\n",c1);printf("c2= %d\n",c2);}
};



void main()
{   
	C *obj3=new(C);
	
	Vabstract(*obj3).set(1000,1000);
	A(*obj3).set(10,10); 
	B(*obj3).set(20,20);
	C(*obj3).set(30,30);
	obj3->set(50,50); //работает правильно

	Vabstract(*obj3).getInfo();
	A(*obj3).getInfo();
	B(*obj3).getInfo();
	C(*obj3).getInfo();
	obj3->getInfo();//работает правильно

	delete(obj3);
};
Результат:

x1= -1163005939
x2= -1163005939
a1= -1163005939
a2= -1163005939
b1= -1163005939
b2= -1163005939
c1= 50
c2= 50

Когда я писал такое наследование (ниже), то привидение к типу объекта было корректным...

Листинг 2:
Код:
#include <stdio.h>

class Resourse{
	private:
		int ResID;
		int Count;
	public:
		void setResID(int aa){ResID=aa;}
		void setCount(int aa){Count=aa;}
		virtual void getInfo(){ 
		printf("Resourse.ResID= %d\n",ResID);
		printf("Resourse.Count= %d\n",Count);}
};

class Player: public Resourse{
	private:
		int NameID;
		int Health;
		int x1;
		int x2;
		int x3;
		int Ammo;
	public:
		void setNameID(int aa){NameID=aa;}
		void setHealth(int aa){Health=aa;}
		void setAmmo(int aa){Ammo=aa;}
	void getInfo(){
		printf("Player.nameID= %d\n",NameID);
		printf("Player.health= %d\n",Health);
		printf("Player.health= %d\n",Ammo);	}
};


void main()
{   
	Player *objPlayer1=new(Player);

	objPlayer1->setNameID(1);
	objPlayer1->setHealth(100);
	objPlayer1->setAmmo(17);
	objPlayer1->getInfo();
	Player(*objPlayer1).getInfo(); //функ. работала правильно

	objPlayer1->setResID(1);
	objPlayer1->setCount(10);
	Resourse(*objPlayer1).getInfo();//функ. работала правильно

	delete(objPlayer1);
};
Не подскажите как правильно привести типы из первого листинга?

Последний раз редактировалось MasterGH; 13.11.2009 в 22:37.
MasterGH вне форума Ответить с цитированием
Старый 13.11.2009, 23:13   #2
pu4koff
Старожил
 
Аватар для pu4koff
 
Регистрация: 22.05.2007
Сообщений: 9,065
По умолчанию

Дело в том, что это не приведение типа. Приведение типов записывается так:
(тип) переменная.
В данном случае надо:
(Vabstract) (*obj3).
А еще лучше использовать родные приведения типов языка С++: static_cast<тип>(переменная), dynamic_cast<тип>(переменная),...

А запись Vabstract(*obj3) - это конструирование нового экземпляра типа Vabstract посредством вызова конструктора копии. Получается, что функция set вызывается для одних временных объектов, а getInfo - для совершенно иных, потому на экран и выходит мусор.
pu4koff вне форума Ответить с цитированием
Старый 14.11.2009, 00:14   #3
MasterGH
Пользователь
 
Аватар для MasterGH
 
Регистрация: 08.11.2009
Сообщений: 16
По умолчанию

Цитата:
Сообщение от pu4koff Посмотреть сообщение
Дело в том, что это не приведение типа. Приведение типов записывается так:
(тип) переменная.
В данном случае надо:
(Vabstract) (*obj3).
Я попробовал в том же листинге:
(Vabstract)(*obj3).set(1000,1000);

Мне выдаёт ошибку:
Цитата:
error C2440: приведение типов: невозможно преобразовать 'void' в 'Vabstract'
Цитата:
Сообщение от pu4koff Посмотреть сообщение
А еще лучше использовать родные приведения типов языка С++: static_cast<тип>(переменная), dynamic_cast<тип>(переменная),...
Буду читать материал по этим шаблонам.

Цитата:
Сообщение от pu4koff Посмотреть сообщение
А запись Vabstract(*obj3) - это конструирование нового экземпляра типа Vabstract посредством вызова конструктора копии. Получается, что функция set вызывается для одних временных объектов, а getInfo - для совершенно иных, потому на экран и выходит мусор.
Тут похоже я вообще сделал грубую ошибку
Спасибо.
В общем полез я опять в отладчик к таблицам виртуальных функций буду следить за регистрами к какой виртуальной таблице происходит обращение. А после буду читать про cast-ы. А вообще не хотелось бы использовать dynamic_cast и собирать, наверно, лишнюю информацию об RTTI ...
MasterGH вне форума Ответить с цитированием
Старый 14.11.2009, 00:50   #4
pu4koff
Старожил
 
Аватар для pu4koff
 
Регистрация: 22.05.2007
Сообщений: 9,065
По умолчанию

Цитата:
Сообщение от MasterGH Посмотреть сообщение
Я попробовал в том же листинге:
(Vabstract)(*obj3).set(1000,1000);

Мне выдаёт ошибку:
Потому что я глупость написал
Типы приводить нужно через указатели.
В си-стиле это:
Код:
((Vabstract*) obj3)->set(1000,1000);
Или в стиле C++:
Код:
static_cast<Vabstract*>(obj3)->set(1000,1000);
Этот же код:
Код:
static_cast<Vabstract>(*obj3).set(1000,1000);
так же создаст на основе obj3 временный объект класса Vabstract.
В качестве теста можно в private/protected часть засунуть реализацию конструктора: Vabstract(Vabstract const &) {}, чтобы компилятор сам её в public'e не создавал и не копировал что не надо. С таким конструктором static_cast<Vabstract> уже не сработает
pu4koff вне форума Ответить с цитированием
Старый 14.11.2009, 00:51   #5
netrino
Участник клуба
 
Аватар для netrino
 
Регистрация: 15.07.2008
Сообщений: 1,933
По умолчанию

Опоздал =)
netrino вне форума Ответить с цитированием
Старый 14.11.2009, 17:11   #6
MasterGH
Пользователь
 
Аватар для MasterGH
 
Регистрация: 08.11.2009
Сообщений: 16
По умолчанию

Я немного переписал программу.. результат тот же - выводится мусор

Код:
#include <stdio.h>

class Vabstract{
	public:
	int x1; int x2;
	virtual void set(int a, int b){x1=a;x2=b;}
	void getInfoX(){printf("x1= %d\n",x1);printf("x2= %d\n",x2);}
};

class A: public virtual  Vabstract{
	public:
	int a1; int a2;
	virtual void set(int a, int b){a1=a;a2=b;}
	void getInfoA(){printf("a1= %d\n",a1);printf("a2= %d\n",a2);}
};

class B: public virtual Vabstract{
	public:
	int b1; int b2;
	virtual void set(int a, int b){b1=a;b2=b;}
	void getInfoB(){printf("b1= %d\n",b1);printf("b2= %d\n",b2);}
};

class C: public A, public B {
	public:
	int c1; int c2;
	virtual void set(int a, int b){c1=a;c2=b;}
	void getInfoC(){printf("c1= %d\n",c1);printf("c2= %d\n",c2);}
};

void main()
{   
	C *obj=new(C);
	//((Vabstract*)obj)->set(1000,1000); - не работает
	static_cast<Vabstract*>(obj)->set(1000,1000); // не работает

	((A*)obj)->set(10,10);//obj->setA(10,10);
	((B*)obj)->set(20,20);//obj->setB(20,20);
	((C*)obj)->set(30,30);//obj->set(40,40);

	obj->getInfoX();
	obj->getInfoA();
	obj->getInfoB();
	obj->getInfoC();

	delete(obj);
};
Результат:
x1= -1163005939
x2= -1163005939
a1= -1163005939
a2= -1163005939
b1= -1163005939
b2= -1163005939
c1= 30
c2= 30

Последний раз редактировалось MasterGH; 14.11.2009 в 17:15.
MasterGH вне форума Ответить с цитированием
Старый 14.11.2009, 17:27   #7
netrino
Участник клуба
 
Аватар для netrino
 
Регистрация: 15.07.2008
Сообщений: 1,933
По умолчанию

Ф-ции ведь виртуальны, а объект был создан типа C, потому все ф-ции set будут вызваны именно для него и инициализированы будут только c1, c2, в остальных останется мусор
netrino вне форума Ответить с цитированием
Старый 14.11.2009, 19:55   #8
MasterGH
Пользователь
 
Аватар для MasterGH
 
Регистрация: 08.11.2009
Сообщений: 16
По умолчанию

netrino, я объяснение не понял.

Проблема как заставить работать виртуальные функции принадлежащие к тому или иному классу внутри иерархии классов, где присутствует виртуальное наследование базового класса в моём последнем примере?
MasterGH вне форума Ответить с цитированием
Старый 14.11.2009, 20:25   #9
netrino
Участник клуба
 
Аватар для netrino
 
Регистрация: 15.07.2008
Сообщений: 1,933
По умолчанию

Суть виртуальных функций состоит в том, чтобы при приведении к базовому классу можно было вызывать методы производного. Например абстрактный класс поток, в котором есть виртуальная функция get, которая берёт 1 байт из потока, производными от этого потока будут, например, файловый поток, строковый поток и сетевой поток. Но так как get виртуальна, я могу написать ф-цию, которая читает строку из потока, при этом объявив, что в качестве параметра она принимает базовый поток, а передавать в неё любой из производных, при этом, в зависимости от полученного потока ф-ция прочитает строку из файла, строки или из сети.
Код:
class basestream {
    virtual char get() = 0;
};

class netstream : public basestream {
    char get() { ... }
};

class stringstream : public basestream {
    char get() { ... }
};

class filestream : public basestream {
    char get() { ... }
};

const char* getline(basestream* stream)
{
    char* str = new char[1000];
    int    i = 0;
    char c = 0;
    while( c != '\n' ) {
        c = stream->get();
        str[i++] = c;
    }

    str[i] = 0;

    return str;
}

int main()
{
    netstream* net = new netstream;
    filestream* file = new filestream;
    stringstream* str = new stringstream;

    getline(net);  // считает строку из сети
    getline(file);  // считает строку из файла
    getline(str);  // считает строку из строки

    return 0;
}
А судя из Вашего предыдущего поста, Вы хотите, чтобы при приведении к классу, вызывалась уже его ф-ция с таким именем? Тогда всё что нужно сделать - убрать ключевое слово virtual из описания метода set у всех классов
netrino вне форума Ответить с цитированием
Старый 15.11.2009, 01:54   #10
MasterGH
Пользователь
 
Аватар для MasterGH
 
Регистрация: 08.11.2009
Сообщений: 16
По умолчанию

Нет, мне нельзя убирать ключевое слово virual, так не сформируется vftable объекта obj.

Я Вам помогу понять мою задачу. Я пишу безвозмездно статью о том, как формируются объекты в памяти (процесса игр) при сложных вариациях иерархий наследования и о том, как можно понять иерархию классов по псевдокоду плагина-декомпилятора Hex Rays из IDA. В данный момент меня интересует, как построятся данные таблицы vftable и vbtable объекта obj, а также структуры данных объекта obj, описание которого я привёл выше... только я не научился корректно вызывать виртуальные функции, т.к. не работал с С++, а только с Дельфи. Не то чтобы "работал", это моё хобби.

Спасибо за пример, показывающий роль виртуальных функций. Я понимаю, что виртуальные функции нужны в работе как минимум с двумя объектами за счёт чего достигает полиморфизм – одна функция и разные объекты. Я не стал описывать классы других объектов, поэтому полиморфизма явно не видно. Считайте так, что каждая функция set оказалась виртуальной (т.к. существуют другие классы, за счёт которых происходит явление полиморфизма) и считайте что функция set должна работать с данными, описанными в том же классе что и виртуальная функция set.

Спасибо за интерес к моей непростой теме =)
MasterGH вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Помощь в классах и функциях. Serror Общие вопросы C/C++ 11 10.10.2009 06:23
Паскаль ООП. Примеры программ с использованием ООП SeЯgey Помощь студентам 5 13.05.2009 21:55
Небольшой этический вопросик о классах. Longedok Помощь студентам 2 04.08.2008 13:23
реализация стратегии на классах mahsus Общие вопросы C/C++ 1 28.12.2007 10:27