Как базовый класс может удовлетворить определение чистой виртуальной функции родителя, используя функцию другого родителя

Я расширяю существующий проект C++. У меня есть базовый класс, производный от двух родительских классов. Один из родителей имеет чисто виртуальную функцию. Я хочу, чтобы эта чистая виртуальная функция определялась функцией, реализованной в другом родителе.

Итак, я хочу, чтобы другой родитель выполнил обязательство базового класса по определению чистой виртуальной функции родителя. Я пробовал два подхода, оба привели к ошибкам компилятора.
Есть идеи?

Вот программа на C++, демонстрирующая мою первую идею, в надежде, что компилятор просто воспользуется определением base2 для vfunc().

// This is my first approach, hoping the parent base2 of derived would satisfy the need to define
// base1's pure virtual vfunc.

class base1 {
public:
 virtual int vfunc() = 0;
};

class base2 {
public:
 int vfunc() { return 0;} //defined
};

class derived : public base1, public base2 {
public:
 //empty
};

int main()
{
 derived d;
 base1 & b1 = d;
 int result = b1.vfunc();
 return result;
}

Компилятор сообщает, что derived по-прежнему является абстрактным классом:

$ gcc a.cc 
a.cc: In function ‘int main()’:
a.cc:26: error: cannot declare variable ‘d’ to be of abstract type ‘derived’
a.cc:18: note:   because the following virtual functions are pure within ‘derived’:
a.cc:7: note:  virtual int base1::vfunc()

Вот моя вторая попытка:

// This is my second attempt, defining a vfunc in the derived class that calls the other parent.

class base1 {
public:
 virtual int vfunc() = 0;
};

class base2 {
public:
 int vfunc() { return 0; } // defined
};

class derived : public base1, public base2 {
public:
 int vfunc() { return base2::vfunc(); } // call the other parent's vfunc
};

int main()
{
 derived d;
 base1 & b1 = d;
 int result = b1.vfunc();
 return result;
} 

На самом деле я ожидал, что это сделает это за меня, но вместо этого компоновщик выдает мне кучу ошибок vtable, которые я не понимаю: ( Mac OS 10.6, gcc 4.2.1 )

$ gcc inheritance_tester.cc 
Undefined symbols:
  "vtable for __cxxabiv1::__vmi_class_type_info", referenced from:
      typeinfo for derivedin ccmeHq8C.o
  "___cxa_pure_virtual", referenced from:
      vtable for base1in ccmeHq8C.o
  "___gxx_personality_v0", referenced from:
      _main in ccmeHq8C.o
      base2::vfunc()     in ccmeHq8C.o
      derived::vfunc()     in ccmeHq8C.o
      base1::base1() in ccmeHq8C.o
      base2::base2() in ccmeHq8C.o
      derived::derived()in ccmeHq8C.o
      CIE in ccmeHq8C.o
  "vtable for __cxxabiv1::__class_type_info", referenced from:
      typeinfo for base1in ccmeHq8C.o
      typeinfo for base2in ccmeHq8C.o
ld: symbol(s) not found

person NoahR    schedule 15.09.2010    source источник
comment
производная vfunc должна быть return base::vfunc();, но это, вероятно, опечатка.   -  person    schedule 16.09.2010
comment
Почему вы пытаетесь дать члену base2 то же имя, что и виртуальной функции? Это более удобно, если вы поставите после него impl или что-то в этом роде.   -  person    schedule 16.09.2010


Ответы (3)


Ваш второй фрагмент кода в порядке, вы просто неправильно его компилируете. Вам нужно скомпилировать с помощью g++, а не gcc. Когда вы компилируете с помощью g++, он автоматически связывается с библиотеками времени выполнения C++; когда вы компилируете с помощью gcc, это не так. Вы также можете вручную добавить их самостоятельно:

# Option 1: compile with g++
g++ inheritance_tester.cc

# Option 2: compile with gcc and link with the C++ standard libraries
gcc inheritancet_test.cc -lstdc++
person Adam Rosenfield    schedule 15.09.2010
comment
Спасибо. Это подходящее решение моей проблемы и исправление для метода 2, который я опубликовал. - person NoahR; 16.09.2010

Вам нужно переопределить vfunc из base1. Вы можете сделать это следующим образом:

class derived : public base1, public base2 {
public:
 using base1::vfunc;
 int vfunc() { return base2::vfunc(); } // call the other parent's vfunc
};
person Kirill V. Lyadvinsky    schedule 15.09.2010
comment
У вас есть ссылка на то, где описывается это использование using? - person ; 16.09.2010
comment
@jdv, §10.2/2 стандарта C++. - person Kirill V. Lyadvinsky; 16.09.2010
comment
Мой опыт показывает, что метод 2 работает с нашим без использования строки base1::vfunc. Я не уверен, для чего предназначена эта линия. - person NoahR; 16.09.2010
comment
Хорошо, я сейчас столкнулся с мудростью Кирилла. Без оператора using другие перегруженные члены vfunc с тем же именем из base2 скрыты от компилятора. Однако в производном мне пришлось использовать использование base2::vfunc; не база1. - person NoahR; 16.09.2010

Эта модифицированная версия derived работала у меня на Visual C++. Для меня это означает, что вы должны явно устранить неоднозначность двух унаследованных vfunc().

class base1 {
public:
    virtual int vfunc() = 0;
};

class base2 {
public:
    int vfunc() { return 0;} //defined
};

class derived : public base1, public base2 {
public:
    int base1::vfunc() { return base2::vfunc(); } // call the other parent's vfunc
};

int main()
{
    derived d;
    base1 & b1 = d;
    int result = b1.vfunc();
    return result;
}
person Steve Townsend    schedule 15.09.2010
comment
Ошибки ld vtable, о которых сообщалось во втором подходе, были вызваны вызовом gcc, а не g++. Дох! Первый метод все еще не компилируется. - person NoahR; 16.09.2010