Множественное наследование (Multiple inheritance)— наследование от нескольких базовых классов одновременно.
Интерфейсы
Интерфейс — специальные классы, описывающие набор методов, но не имеющие
данных и реализации. Например интерфейсами являются классы:
Наследование от нескольких полноценных классов
Допустим у нас есть классы Cow и Sniper, а мы хотим получить класс, который
обладает данными и методами обоих классов. Назовем его CowSniper. Таким образом
у нас есть возможность «скрещивать» классы. 
Таким образом синтаксически множественное наследование почти не отличается от
обычного.
class CowSniper: public Cow, public Sniper {
};
Заметим , что важен порядок перечисления предков, потому как в
зависимости от него:

Перекрытие имен функций
	
эта проблема есть как и в обычном наследовании, так и в
	множественном.
Пусть в классах Cow и Sniper были методы sleep(). 
	
	Тогда код:
CowSniper cs;
cs.sleep(); // 2
	В строке 2 произойдет ошибка компиляции, т. к. компилятор не может
	выбрать какой
	метод sleep() ему вызывать (от Cow или от Sniper). Поэтому необходимо
	сообщимть ему правильный выбор: cs.Cow::sleep();
Перекрытие виртуальных функций
Теперь рассмотрим тот
	случай, если метод sleep() виртуальный в классах-предках.
class Cow {
public:
    virtual void sleep() = 0;
};
class Sniper {
public:
    virtual void sleep() = 0;
};
class CowSniper: public Cow, public Sniper {
public:
    virtual void sleep() {}
};В таком случае будут перегружены сразу оба метода (ведь у них одинаковая сигнатура). Подробнее о том почему это происходит написано в следующем пункте. Тогда в следующих строках кода:
Cow & c = cs;
Sniper & s = cs;
c.sleep(); //3
s.sleep(); //4
  В строках 3 и 4 вызовется один и тот же метод CowSniper::sleep();
  
С одной стороны это удобно, т. к. если методы называются одинаково, 
  то скорее всего они делают что-то похожее. Тогда перегрузив один метод мы 
  перегрузим сразу оба.
С другой стороны может возникнуть проблема, если 
  класс-потомок должен реализовывать один и тот же виртуальный метод от
  нескольких базовых классов по-разному. Тогда такая перегрузка будет вредна,
  но стандартно по другому не поступить. В таких случаях используется следующий
  способ обойти это ограничение.
Если есть иерархия классов A,B,C. И в
  классах A и B есть некоторый виртуальный метод f().
  
Добавим в эту иерархию еще два класса A1
  и B1. В классах A1 и B1 создадим методы fA() и fB()
  соотвественно. Теперь в C будут методы fA() и fB(),
  которые необходимо перегрузить, причем код в них разный ;)
  
class A{
public:
    virtual void f();
};
class B{
public:
    virtual void f();
};
class A1: public A{
public:
    virtual void f() {fA();}
    virtual void fA() = 0;
};
class B1: public B{
public:
    virtual void f() {fB();}
    virtual void fB() = 0;
};
class C: public A1, public B1 {
public:
    virtual void fA();
    virtual void fB();
};Теперь использовать эти классы можно так:
C  c;
A & a = c;
B & b = c;
a.f(); //5
b.f(); //6
  В строке 5 вызовется C::fA(), а в строке 6 -
C::fB(). Обратите внимание на то, как перегружен метод
f() в классах A1 и B1, которые ко всему прочему теперь являются
абстрактными и невозможно создать их экземпляры. Цель достигнута.
Представление объекта в памяти
  
а) Как мы помним, в случае линейного наследования распределение
полей классов для объекта класса наследника в памяти будет такое:

  
При этом если мы создадим три указателя:
C * = new C();
B * b = c;
A * a = c;
То указывать они все будут на начало объекта в памяти: 
и при этом все хорошо.
б) Теперь перейдем к рассмотрению
простейшего примера множественного наследования:
При этом если создать объект класса C, то в памяти он будет выглядеть
так:
Опять попробуем создать три указателя как и в случае линейного
наследования:
C * = new C();
B * b = c;
A * a = c;
	
Однако теперь указатели
	распределятся следующим образом:
	
Мы видим, что классы A и B фактически
	разделены, но ведь если они имеют
	виртуальные методы, то должны вызываться
	перегруженные виртуальные методы
	класса C. Для того, чтобы это происходило
	в каждом из них есть ссылка на таблицу
	виртуальных функций. Тогда получается
	что в классе C будет сразу два указателя
	на две различные таблицы виртуальных
	функций (кол-во указателей = кол-ву полиморфных предков). 
	Получается, что таблиц виртуальных функций две. Это не совсем
	так, т. к. они просто лежат рядом в
	памяти, т. е. фактически таблица одна,
	но в ней могут быть повторения, например, виртуальный деструктор:
	
в) Теперь рассмотрим множественного
	наследования, если в графе наследования
	есть цикл:
В памяти объект
класса D будет представлен так:
Возникает проблема дублирования полей класса A, т.к. фактически будет два
разных объекта типа A. Значит при компиляции следующего кода произойдет ошибка,
т.к. непонятно к какому полю обращаться. 
Для того, чтобы указать, какой из классов A выбрать следует написать так:
D * d = new D();
A * a = d;
При этом если мы хотим использовать методы класса A, так же необходимо явно
указывать которого их них. Вследствие этого даные в двух объектах A в пределах
одного D могут стать различными.
D * d = new D();
A * a = static_cast<B*> (d);
Рассмотрим два случая такого
наследования и посмотрим на возможные проблемы с наличием двойного объекта.
1) Такой эффект может быть полезен, если класс A является файловым потоком,
а B и C это writer и reader соответственно. А класс D читает из С и пишет в B.
Очевидно, что у B и C файлы могут быть различны (скорее всего так и есть).
2) Рассмотрим умный указатель (A = LinkCounter), который внутри себя
сдержит счетчик. В таком случае в классе D возникает два счетчика, что может
привести к печальным последствием, если в одном месте работать с одним из
них, а в другом с другим.
Вообще такое наследование (с циклами в графе
родства) называется бриллиантовым (diamond inheritance)
или Ромбовидным.
Виртуальное
	наследование.
Рассмотрим его на
	примере следующей иерархии
	
Синтаксически виртуальное
	наследование почти не отличается от
	множественного:
class A{
    int k;
};
class B: public virtual A {
};
class C: public virtual A {
};
class D: public B, public C {
};
При этом следующий код
	будет прекрасно работать:
А все потому, что классы будут выглядеть следующим образом:
D * d = new D();
A * a = d;
Здась может появиться проблема преобразования указателя на D к указателю на
C. Но этой проблемы нет. Необходимо разобраться каким образом это реализуется.
Дело в том, что при виртуальном наследовании добавляется виртуальная
функция, возвращающая указатель на A. Фактически для программиста она не видна.
Для пояснения обозначим ее за getA(). Стоит заметить что она будет различна в
классах B,C и D.
Теперь при обращении к полю из A (допустим в нем поле
int k) код вида k = 10; будет автоматически
преобразован в getA()->k = 10;.
Теперь рассмотрим очередность вызова конструкторов.
Логично предположить, что вызов конструкторов пройдет так 
Но возникает проблема, ведь конструкторы B и C могут вызывать различные
конструкторы A и с различными параметрами.
Для определенности было введено следующее правило: Конструктор A должен
быть явно вызван в конструкторе D, при этом в конструкторах B и C вызов
конструктора A опустится.
В связи с этим есть замечание: Нужно следить и понимать, что при
виртуальном наследовании в конструкторах B и C может не вызваться конструктор A
с разными параметрами.
В заключении можно сказать, что реализации множественного наследования ведут к появлению сильных зависимостей в коде, а следовательно такое наследование желательно использовать только с интерфейсами.
Возможной заменой множественного наследования
не от интерфейсов является агрегация:
class CowSniper {
private:
    Cow c;
    Sniper s;
};