2015-02-22 3 views
2

Вот мои занятия. Это простоМножественное наследование без переопределения функции. Почему двусмысленность

#include<iostream> 
using namespace std; 

class Base 
{ 
public: 
    int var; 

    Base(int val) : var(val) 
    {} 

    void foo() 
    { 
     cout << "Class Base foo()" << endl; 
    } 
}; 

class Derived_A : public Base 
{ 
public: 
    Derived_A(int val) : Base(val) 
    {} 
}; 

class Derived_B : public Base 
{ 
public: 
    Derived_B(int val) : Base(val) 
    {} 
}; 

class Derived_AB : public Derived_A, public Derived_B 
{ 
public: 
    Derived_AB(int var1, int var2) : Derived_A(var1), Derived_B(var2) 
    {} 
}; 

int main(int argc, char** argv) 
{ 
    Derived_AB ABDerObj(1,2); 
    ABDerObj.foo(); 
    return 0; 
} 

И это дает мне ошибку компилятора говоря, вызов Foo неоднозначно.

Функция foo не переопределяется нигде. Я понимаю, что есть два экземпляра класса Base, потому что виртуального наследования нет. Таким образом, есть две переменные var. Но я не понимаю, почему компилятор имеет двусмысленность в вызове foo. Функции не зависят от экземпляра. Они относятся к классу. Почему компилятор дает сообщение об ошибке?

Я использую Visual Studio 2013.

Спасибо.

+0

Это то, на что наследулось «виртуальное» наследование в текущем стандарте. –

+1

«Функции не специфичны для конкретного экземпляра» - вы ошибаетесь, они есть. –

ответ

3

Множественное наследование делает вы унаследовали два раза функции Foo(): один как часть Derived_A и один в рамках Derived_B, потому что каждый его наследует свой собственный BaseClass.

BaseClass  BaseClass 
    |    | 
Derived_A  Derived_A 
    \   /
     \  /
     Derived_AB 

Так что компилятор не знает, если он будет выполнять его с данными Derived_A или Derived_B подобъекте.

Решения:

1) Вы можете неоднозначность явно в каждом позвоню сделать:

ABDerObj.Derived_A::foo(); // execute foo() for the Deived_A object 

2) Вы можете неоднозначность явно на уровне класса, определяя общую Foo, например, :

void foo() 
{ 
    Derived_A::foo(); // you want all the foos called 
    Derived_B::foo(); 
} 

3) Если вы хотите иметь только один BaseClass для Derived_AB, то вы должны сделать BaseClass виртуальным базовым классом для Derived_A и Derived_B. Тогда Derived_AB будет иметь только один сингл BaseClass, а звонок foo() больше не является двусмысленным.

 BaseClass 
    /  \ 
    /  \ 
Derived_A  Derived_A 
    \   /
     \  /
     Derived_AB 

(приписка в этом случае, вы должны также определить BaseClass() инициализатор для Dervived_AB.)

+1

Спасибо. Мое понимание заключалось в том, что для каждой функции-члена класса существует только одна копия функции. И компилятор неявно передает '* this' каждой функции-члену, поэтому функции-члены могут обращаться к конкретным членам экземпляра. Не является ли также причиной, почему вы можете просто сделать memcopy объекта (POD) для создания нового объекта? – madu

+1

@madu Действительно, нестатические функции-члены имеют неявный параметр 'this'. ** Нет такой вещи, как «копия» функции. ** Функции не являются объектами. ** Функции-члены не являются членами данных ** и не существуют внутри экземпляров объектов. 'memcpy' обрабатывает необработанную память объекта POD, то есть членов данных класса. Вы можете распечатать отдельные байты представления некоторых объектов, вы не можете распечатать функцию. Указатель на функцию не является указателем на объект. Вы не можете преобразовать указатель на функцию в 'char *' и использовать результат как таковой. – curiousguy

+1

@madu технически, существует только один код вашей функции. Но когда компилятор генерирует вызов функции-члена, он предоставляет ему «этот» указатель на объект BaseClass. В вашем примере, если вы не сделали это виртуальным наследованием, есть два разных подобъекта. – Christophe

2

функция не является экземпляром специфичен. Они относятся к классу.

Это справедливо только для статических функций. Предположим, что я изменить реализацию foo() на следующее:

void foo() 
{ 
    cout << "My value is " << var << endl; 
} 

Что вы ожидаете ABDerObj.foo(); напечатать?


Поскольку это ответ, позвольте мне также предоставить два возможных решения.

Если foo действительно, специфичная (например, потому что он использует поле класса var, вы можете легко определить компилятор, который Base::foo() вы хотите позвонить (в Derived_A или версию Derived_B), указав, какой вы хотите использовать

ABDerObj.Derived_A.foo();  // prints: My value is 1 
ABDerObj.Derived_B.foo();  // prints: My value is 2 

или, просто выбрасывая другую часть:

((Derived_A) ABDerObj).foo(); // prints: My value is 1 
((Derived_B) ABDerObj).foo(); // prints: My value is 2 

Последнее в основном полезен, если у вас есть блок кода где вы собираетесь использовать толькоDerived_A функциональность, например:

Derived_A& ABDerObjPartA = ABDerObj; 
ABDerObjPartA.foo(); 
// more code using ABDerObjPartA. 

С другой стороны, если - придерживаться вашей терминологии - определение foo действительно зависит от класса, а не на экземпляр, сделать его static:

// In Base: 
static void foo() 
{ 
    cout << "Class Base foo()" << endl; 
} 

// in main(): 
ABDerObj.foo(); // This is now OK 

Что вы должны действительно смотреть в ли вы на самом деле потребность множественное наследование, особенно с го один и тот же базовый класс, встречающийся более одного раза. Часто, как только вы начинаете сталкиваться с этими проблемами неоднозначности, они продолжают ухудшаться, а небольшая редизайн вашего кода сделает его гораздо более удобным.

+0

Спасибо. Мое понимание заключалось в том, что каждый класс нестатического класса передается '* this' в качестве параметра компилятором, поэтому вы можете сделать «cout <<». Мое значение «<< var». Итак, каждый экземпляр класса имеет свою собственную копию каждой функции-члена? – madu

Смежные вопросы