Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1] 2  Все   Вниз
  Печать  
Автор Тема: Множественное наследование и виртуальные функции  (Прочитано 36088 раз)
0 Пользователей и 2 Гостей смотрят эту тему.
Алик
Постоялец

kz
Offline Offline

« : 02-11-2004 11:59 » 

Есть два виртуальных класса (имена изменены, любое сходство случайно Улыбаюсь)

Код:
class A{
...
virtual void userproc(int result){};
};

class B{
...
virtual void userproc(int result){};
};
Эти классы что-то вычисляют, и вызывают userproc с результатом вычислений в качестве параметра.
потомки классов перекрывают юзерпроки своими полезными процедурами.

Теперь появилась нужда в классе
Код:
class AB:public A,public B{
<виртуальная функция, замещающая A::userproc>
<виртуальная функция, замещающая B::userproc>
}
Это как-нибудь можно реализовать? Что-то я по Страуструпу глазами пробежал - не нашел... Жаль
« Последнее редактирование: 02-12-2007 16:59 от Алексей1153++ » Записан
Pu
Большой босс

ru
Offline Offline
78


« Ответ #1 : 02-11-2004 12:11 » 

не очень понял Жаль. может так:
Код:
class AB:public A,public B
{
 virtual void userprocA(int result)
 {
    A::userproc(result);
 }
 virtual void userprocB(int result)
 {
   B::userproc(result);
 }
};
« Последнее редактирование: 02-12-2007 17:00 от Алексей1153++ » Записан

Насколько я опытен? Достаточно, чтобы понимать, что дураков нельзя заставить думать по–другому, но недостаточно, чтобы отказаться от попыток это сделать.
(с) Артур Джонс
Алик
Постоялец

kz
Offline Offline

« Ответ #2 : 02-11-2004 13:59 » 

Цитата: Pu
не очень понял Жаль. может так:
Код:
class AB:public A,public B
{
 virtual void userprocA(int result)
 {
    A::userproc(result);
 }
 virtual void userprocB(int result)
 {
   B::userproc(result);
 }
};



нет, так не пойдет. Виртуальные функции вызываются из других методов классов A и B. Что-то типа этого:
Код:
class A{
virtual void userproc(int result){};
void main(){
...
userproc(15);
}
}

И идея в том, что процедура main должна вызывать AB::<виртуальная функция, замещающая A::userproc>
« Последнее редактирование: 02-12-2007 17:01 от Алексей1153++ » Записан
Pu
Большой босс

ru
Offline Offline
78


« Ответ #3 : 02-11-2004 14:22 » 

Алик, а для какого объекта, А или АВ? Что то я тебя не понял.
Записан

Насколько я опытен? Достаточно, чтобы понимать, что дураков нельзя заставить думать по–другому, но недостаточно, чтобы отказаться от попыток это сделать.
(с) Артур Джонс
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #4 : 02-11-2004 14:28 » 

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

Может быть тогда найдётся более изящный путь?
Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
Алик
Постоялец

kz
Offline Offline

« Ответ #5 : 02-11-2004 15:12 » 

Есть класс, который перебирает все возможные комбинации  чисел от 0 до x.
Для каждой последовательности вызывается виртуальная процедура void userproc(vector<DWORD>& v) (в векторе - комбинация).  В этом классе она ничего не делает и должна быть перекрыта в потомке (и действовать в зависимости от нужд пользователя).
И второй класс действует по такому же принципу. Производный класс использует функциональность обоих классов.
Вот.
Записан
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #6 : 02-11-2004 15:25 » 

Алик, что значит "использует" ?

Если тебе надо вызывать функции базовых классов, то обращайся к ним как A::userproc и B::userproc

Более того, достаточно сделать абстрактный базовый класс и унаследовать A, В и АВ от него.

Код:
class base_C {
public:
    virtual void userproc(int x) = 0;
};

class A : public base_C {
public:
    virtual void userproc(int );
};

class B : public base_C {
public:
    virtual void userproc(int );
};

class C : public base_C {
    A _a;
    B _b;
public:
    virtual void userproc(int );
       
};

void C::userproc(int x) {
    _a.userproc(x);
    _b.userproc(x);
}
« Последнее редактирование: 02-12-2007 17:02 от Алексей1153++ » Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
Алик
Постоялец

kz
Offline Offline

« Ответ #7 : 03-11-2004 03:12 » 

npak, в твоем коде уже не множественное наследование (вообще не наследование). Я знаю, что есть этот вариант.
Но мне интересно, дает ли язык возможность осуществить мой замысел именно через множественное наследование, иначе придется объявлять 2 класса, производных от А и B с перекрытым методом userproc, и уже экземпляры этих классов делать членами класса C, что, на мой взгляд, не очень элегантно.
Записан
Pu
Большой босс

ru
Offline Offline
78


« Ответ #8 : 03-11-2004 06:39 » 

Алик, мдааааааа  Я шокирован!   Вот такой я вот  :new_shot:
Записан

Насколько я опытен? Достаточно, чтобы понимать, что дураков нельзя заставить думать по–другому, но недостаточно, чтобы отказаться от попыток это сделать.
(с) Артур Джонс
Алик
Постоялец

kz
Offline Offline

« Ответ #9 : 03-11-2004 06:44 » 

Я ваще такой. люблю о смысле жизни поговорить... Улыбаюсь
Записан
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #10 : 03-11-2004 08:04 » 

Алик, этой загадочной фразы я просто не понял
Цитата
Есть два виртуальных класса
особенно если после этого следует такой код
Цитата
class A{
...
virtual void userproc(int result){};
};

class B{
...
virtual void userproc(int result){};
};
Идем дальше, ты пишешь
Цитата
Теперь появилась нужда в классе
Код:
class AB:public A,public B{
<виртуальная функция, замещающая A::userproc>
<виртуальная функция, замещающая B::userproc>
}
У меня тут вопрос, в класса AB должны быть ДВЕ функции (одна из которых замещаяет A::userproc, а вторая B::userproc) или одна???
вот посмотри сюда может тебе надо это
Код:
class Abstruct
{
public:
virtual ~Abstruct() {}
virtual void f(const char * s) = 0;
};

class A : public Abstruct
{
public:
void fa(const char * s)
{
// ...
f(s);
}
};

class B : public Abstruct
{
public:
void fb(const char * s)
{
// ...
f(s);
}
};

class AB : public A, public B
{
public:
void f(const char * s)
{
s = "do something...";
}
};

void main()
{
AB ab;
ab.fa("fa");
ab.fb("fb");
}
« Последнее редактирование: 02-12-2007 17:03 от Алексей1153++ » Записан

С уважением Lapulya
Алик
Постоялец

kz
Offline Offline

« Ответ #11 : 03-11-2004 08:23 » 

Цитата: lapulya
Алик, этой загадочной фразы я просто не понял
Цитата
Есть два виртуальных класса
особенно если после этого следует такой код
Цитата
class A{
...
virtual void userproc(int result){};
};

class B{
...
virtual void userproc(int result){};
};
Идем дальше, ты пишешь
Цитата
Теперь появилась нужда в классе
Код:
class AB:public A,public B{
<виртуальная функция, замещающая A::userproc>
<виртуальная функция, замещающая B::userproc>
}
У меня тут вопрос, в класса AB должны быть ДВЕ функции (одна из которых замещаяет A::userproc, а вторая B::userproc) или одна???
вот посмотри сюда может тебе надо это
Код:
class Abstruct
{
public:
virtual ~Abstruct() {}
virtual void f(const char * s) = 0;
};

class A : public Abstruct
{
public:
void fa(const char * s)
{
// ...
f(s);
}
};

class B : public Abstruct
{
public:
void fb(const char * s)
{
// ...
f(s);
}
};

class AB : public A, public B
{
public:
void f(const char * s)
{
s = "do something...";
}
};

void main()
{
AB ab;
ab.fa("fa");
ab.fb("fb");
}


Виртуальный класс - класс, имеющий виртуальные методы (то есть, имеющий vtbl).  Не путать с виртуальным базовым классом. Что тебе непонятно в моей фразе?


Да, мне нужно, чтобы класс потомок имел ДВА метода, каждый из которых перекрывал бы виртуальный метод одного из классов-предков.
Единственная загвоздка - эти виртуальные методы имеют одинаковые имена. Как это обойти?
« Последнее редактирование: 02-12-2007 17:04 от Алексей1153++ » Записан
Pu
Большой босс

ru
Offline Offline
78


« Ответ #12 : 03-11-2004 08:26 » 

lapulya, дело в том что эти функции в А и В НИЧЕГО НЕ ДЕЛАЮТ. Те это просто  virtual void userproc(int result) = 0; как я понимаю,(цитата - " В этом классе она ничего не делает и должна быть перекрыта в потомке") а подменить в потомках я не вижу никакой сложности, хоть трижды наследуй. Посему понял что смысл вопроса - сколько будет дважды два четыре. И ответ соответствующий TRUE.  Отлично
Записан

Насколько я опытен? Достаточно, чтобы понимать, что дураков нельзя заставить думать по–другому, но недостаточно, чтобы отказаться от попыток это сделать.
(с) Артур Джонс
Алик
Постоялец

kz
Offline Offline

« Ответ #13 : 03-11-2004 08:37 » 

Таварищи, ну граждане родненькие!
ну где дважды два??
Pu, ну напиши код для примера, если все так просто!
Записан
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #14 : 03-11-2004 08:42 » 

Алик,
Цитата

Виртуальный класс - класс, имеющий виртуальные методы (то есть, имеющий vtbl).

Это где же ты такое определение увидал то Я шокирован!  Я шокирован!  Я шокирован!
ну а вот так
Цитата

Да, мне нужно, чтобы класс потомок имел ДВА метода, каждый из которых перекрывал бы виртуальный метод одного из классов-предков.
Единственная загвоздка - эти виртуальные методы имеют одинаковые имена. Как это обойти?

сделать нельзя!

Pu,
Цитата

дело в том что эти функции в А и В НИЧЕГО НЕ ДЕЛАЮТ

Дык!!! они и у меня ничего не делают...вот смотри, функция
Цитата

virtual void f(const char * s) = 0;

объавлена в классе Abstract (это аналог virtual void userproc(int result) = 0; ), а в классе А и В объявление этих функций присутствует только косвенно т.к. они оба наследники австрактного класса Abstract, и естественно ни А ни В не реализуют эту функцию...
Записан

С уважением Lapulya
Алик
Постоялец

kz
Offline Offline

« Ответ #15 : 03-11-2004 08:54 » 

Цитата
Это где же ты такое определение увидал то    

Вроде, у Страуструпа где-то.

Цитата
ну а вот так

Цитата
Да, мне нужно, чтобы класс потомок имел ДВА метода, каждый из которых перекрывал бы виртуальный метод одного из классов-предков.
Единственная загвоздка - эти виртуальные методы имеют одинаковые имена. Как это обойти?


сделать нельзя!


Ну я так и подозревал. Придется изворачиваться...

lapulya,
Твой пример с классом Abstract делает не то, что мне нужно. Там оба виртуальных метода предков перекрываются одним методом потомка.
Записан
Pu
Большой босс

ru
Offline Offline
78


« Ответ #16 : 03-11-2004 09:14 » 

Алик, а зачем два метода  Я шокирован! . Ты в одной области видимости НИКОГДА не получишь две функции с одной сигнатурой, к бабке не ходи.
может повторюсь уж звиняйте
Код:
class A
{
public:
int x;
A(int v) : x(v) {}
virtual void f(int i) = 0; // ничего не делает
};

class B
{
public:
int x;
B(int v) : x(v) {}
virtual void f(int i) = 0; // ничего не делает тоже
};

class AB : public A, public B
{
public:
AB( int va, int vb) : A(va), B(vb) {}
virtual void f(int i) // делает чтото и закрывает (тоесть вообще) функции в базовых классах , и дает доступ к любым их паблик и протект членам
{
i = A::x + B::x;
}
};


int _tmain(int argc, _TCHAR* argv[])
{
AB ab(2,3);
ab.f( 44);
return 0;
}
« Последнее редактирование: 02-12-2007 17:05 от Алексей1153++ » Записан

Насколько я опытен? Достаточно, чтобы понимать, что дураков нельзя заставить думать по–другому, но недостаточно, чтобы отказаться от попыток это сделать.
(с) Артур Джонс
Алик
Постоялец

kz
Offline Offline

« Ответ #17 : 03-11-2004 09:30 » 

Pu,
Твой пример делает не то, что мне нужно. Там ОБА виртуальных метода предков перекрываются ОДНИМ методом потомка.
Записан
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #18 : 03-11-2004 09:36 » 

Алик,

В С++ есть возможность вызвать метод класса предка.

Код:
AB ab;
ab.A::userproc(1); // Вызов метода предка A
ab.B::userproc(2); // Вызов метода предка B

Правильно ли я понимаю, что ты хочешь, чтобы в выражении ab.B::userproc(1) вызывался не тот метод, который определён для класса B, а некоторый другой, переопределённый?

Ответ: так нельзя.  Квалификация имени специально придумана, чтобы обходить перегрузку имён и вызывать метод предка.

Теперь о множественном наследовании.  Я не вижу никаких выгод в данном случае.  По-моему, агрегация более уместна.  Возможно, это убеждение сформировалось у меня после Java, но я стараюсь избегать в С++ множественного наследования.
« Последнее редактирование: 02-12-2007 17:06 от Алексей1153++ » Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
Алик
Постоялец

kz
Offline Offline

« Ответ #19 : 03-11-2004 09:44 » 

npak, Я согласен, что множественное наследование нужно стараться обходить. Но в моем случае методы обоих классов-предков (те самые, перегруженные в потомке) используют одни и те же данные, являющиеся членами класса-потомка. И обеспечить к этим данным доступ в случае агрегации паревно.
Записан
Алик
Постоялец

kz
Offline Offline

« Ответ #20 : 03-11-2004 09:56 » 

В общем, я эту проблему так решил:


Код:
class tcombinations{
...
virtual void userproc(const vector<DWORD>& v){};
...

};

class tsumitems{
...
virtual void userproc(const vector<DWORD>& v){};
...
};

class tsgsumitems:public tsumitems{
void userproc(const vector<DWORD>& v)|si_userproc(v);};
virtual void si_userproc(const vector<DWORD>& v){};
};


class tsgcombinations:public tcombinations{
        void userproc(const vector<DWORD>& v)|cb_userproc(v);};
        virtual void cb_userproc(const vector<DWORD>& v){};
};

class tsumgroupper:public tsgcombinations,public tsgsumitems{
...
//эти два метода, фактически, перекрывают методы userproc классов tsumitems и tcombinations
        void si_userproc(const vector<DWORD>& v){
            //ду самфин
        };
        void cb_userproc(const vector<DWORD>& v){
            //ду самфин элс
        };
...
};
« Последнее редактирование: 02-12-2007 17:08 от Алексей1153++ » Записан
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #21 : 03-11-2004 09:58 » 

Алик,
Код:
class AB
{
   friend class A;
   friend class B;

   A * a;
   B * b;
};
и ни какой парильни...
« Последнее редактирование: 02-12-2007 17:10 от Алексей1153++ » Записан

С уважением Lapulya
Алик
Постоялец

kz
Offline Offline

« Ответ #22 : 03-11-2004 10:02 » 

lapulya
А доступ к членам AB?
Записан
Pu
Большой босс

ru
Offline Offline
78


« Ответ #23 : 03-11-2004 10:07 » 

Алик, мне почему-то каэться, что у тебя проблемы с проектированием классов, чтобы предки использовали данные потомков   Я шокирован! . Да они и знать не должны что там у них есть какието производные классы!  Изврат, имхо, и полиморфизм вывернутый наизнанку.
Записан

Насколько я опытен? Достаточно, чтобы понимать, что дураков нельзя заставить думать по–другому, но недостаточно, чтобы отказаться от попыток это сделать.
(с) Артур Джонс
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #24 : 03-11-2004 10:09 » 

Алик, ХАХАХАХАХАХАХАХА!!!!!!!!!!!!!!!!!!!!!!!

я вот это
Код:
class A
{
public:
virtual void f(const char * s)  = 0;
void fa(const char * s)
{
f(s);
}
};

class B
{
public:
virtual void f(const char * s)  = 0;
void fb(const char * s)
{
f(s);
}
};

class AA : public A
{
public:
virtual void faa(const char * s) = 0;
void f(const char * s) { faa(s); };
};

class BB : public B
{
public:
virtual void fbb(const char * s) = 0;
void f(const char * s) { fbb(s); };
};

class AB : public AA, public BB
{
public:
void faa(const char * s)
{
s = "do something...";
}

void fbb(const char * s)
{
s = "do something...";
}
};

void main()
{
AB ab;
ab.fa("fa");
ab.fb("fb");
}
написал полтора часа назад .... но потом подумал что это полнейший калл и решил что эта писанина не достойна того чтобы на нее смотреть...
« Последнее редактирование: 02-12-2007 17:11 от Алексей1153++ » Записан

С уважением Lapulya
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #25 : 03-11-2004 10:14 » 

Алик, При множественном наследовании объект включает в себя подобъекты, по одному для каждого класса-предка.  Методы классов-предков используют данные, которые приндлежат к под-объектам!  Они не имеют доступа к данным объекта!

Вот тебе пример:
Код:
#include <iostream>

class A {
    int _x;
public:
    virtual void f(int x) { _x = x; std::cout << "Class A: " << x << "\n"; }
};

class B {
    int _x;
public:
    virtual void f(int x) { _x = x; std::cout << "Class B: " << x << "\n"; }
};

class AB : public A, public B {
public:
    virtual void f(int x) { _x = x; std::cout << "Class C: " << x << "\n"; }
};

Он не компилируется, потому что в AB::f не указано, какую переменную _x брать -- из подобъекта класса A или из подобъекта класса B.  Если ты введёшь в класс AB переменную int _x, то методы A::f и B::f её НЕ УВИДЯТ!

При вызове ab.A::f будет изменено поле ab.A::_x, при вызове ab.B::f будет изменено поле ab.B::_x, но поле ab._x меняться не будет.

По этой причине доступ к состоянию потомка для методов-предков не будет.

В языке Eiffel есть фича, названная renaming -- при наследовании можно указать, как назвать метод предка в потомке.  Эта фича позволяет избегать конфликта имён полей и методов при множественном наследовании. В С++ такой фичи нет.  Надо ручками вводить промежуточные классы, в которых переопределяются методы, и множественно наследовать от промежуточных классов.  Пример см.
http://cpptips.hyperformix.com/cpptips/rename_virt_mi
« Последнее редактирование: 02-12-2007 17:13 от Алексей1153++ » Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #26 : 03-11-2004 10:15 » 

Алик,
Цитата
А доступ к членам AB?
да, если написать так
Код:
class AB 
{
   friend class A;
   friend class B;

   A * a;
   B * b;
};

то у А есть доступ к членам АВ
« Последнее редактирование: 02-12-2007 17:14 от Алексей1153++ » Записан

С уважением Lapulya
Алик
Постоялец

kz
Offline Offline

« Ответ #27 : 03-11-2004 10:36 » 

lapulya,  Жжешь
Только не "class AB : public A, public B ", а "class AB : public AA, public BB"
Ну, это извраты за неимением красивого решения...

Цитата: Pu
Алик, мне почему-то каэться, что у тебя проблемы с проектированием классов, чтобы предки использовали данные потомков   Я шокирован! . Да они и знать не должны что там у них есть какието производные классы!  Изврат, имхо, и полиморфизм вывернутый наизнанку.

В моих классах(-предках) виртуальные функции вызываются для того, чтобы вернуть результат. Конкретнее, один из классов возвращает различные комбинации чисел. И каджую комбинацию надо вернуть пользователю. Можно сделать немеренно большой массив этих комбинаций. А можно возвращать их по одной, что и делается с помощью вызова виртуального метода. Этот виртуальный метод пользует комбинации как ему надо, естественно, используя и другие члены дочернего класса.
(надеюсь, никого в ступор не ввел Отлично )

npak
Цитата

В языке Eiffel есть фича, названная renaming -- при наследовании можно указать, как назвать метод предка в потомке. Эта фича позволяет избегать конфликта имён полей и методов при множественном наследовании. В С++ такой фичи нет. Надо ручками вводить промежуточные классы, в которых переопределяются методы, и множественно наследовать от промежуточных классов.


Вот-вот, к такому печальному выводу я и пришел...
Записан
Алик
Постоялец

kz
Offline Offline

« Ответ #28 : 03-11-2004 10:40 » 

Цитата: lapulya
Алик,
Цитата
А доступ к членам AB?
да, если написать так
Код:
class AB 
{
   friend class A;
   friend class B;

   A * a;
   B * b;
};

то у А есть доступ к членам АВ

Да, но чтобы метод класса А получил доступ к членам класса AB, ему нужно передать ссылку на этот экземпляр AB, что не есть красиво, а есть то же изврат
« Последнее редактирование: 02-12-2007 17:15 от Алексей1153++ » Записан
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #29 : 03-11-2004 10:48 » 

Алик, Да, я уже поправил (и дал полный оригинал того, что было у меня прикинуто на эту тему)

И все же это решение далеко от идеала... так что советую как прислушаться к замечанию зайца
Цитата

Алик, мне почему-то каэться, что у тебя проблемы с проектированием классов

рекомендую перепроектировать этот кусок (так сказать сделать локальный рефакторинг)
Записан

С уважением Lapulya
Страниц: [1] 2  Все   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines