В чем разница между дружбой и наследством?

Предположим, что есть два класса A и B:

class A {};
class B {};

Чем отличаются два приведенных ниже примера?

Пример 1:

class C : public A, public B {}; 

Пример 2:

class C
{
    //private
    friend class A;
    friend class B;
}

person deepspace    schedule 06.08.2013    source источник
comment
Попробуйте вызвать методы A и B на C с дружественной версией.   -  person Borgleader    schedule 06.08.2013
comment
Оба понятия совершенно разные. Так что разница во всем.   -  person juanchopanza    schedule 06.08.2013
comment
stackoverflow.com/questions/388242 /   -  person Benjamin Lindley    schedule 06.08.2013
comment
Ваши друзья не будут так сердиться, если вы не дадите им ничего в своем завещании.   -  person JAB    schedule 06.08.2013
comment
что в них одинакового??   -  person Saksham    schedule 06.08.2013
comment
@JAB: За исключением C++, вы, друзья, можете забрать у вас все, пока вы еще живы, а ваши потомки не могут... C++ — это странный мир, где дружба дает больше, чем отношения между родителями и детьми.   -  person David Rodríguez - dribeas    schedule 06.08.2013
comment
Разница в том, что произойдет, если вы умрете без завещания.   -  person Hot Licks    schedule 06.08.2013


Ответы (4)


friend может касаться private частей (каламбур только слегка преднамеренно! ;) ) того, с чем он дружит, но ничто из A и B не является частью C - это просто означает, что "A и B могут касаться личных битов C"). Все "меньше", чем private, конечно, также доступно для A и B, поэтому, если C состоит из protected или public членов, это также будет доступно.

Когда вы наследуете, A и B становятся частью C. Любые private разделы A и B недоступны для C. В номенклатуре «является-а» и «имеет-а» C теперь является A и является B — другими словами, он унаследован от A, поэтому он «ведет себя как A с точки зрения интерфейса.

person Mats Petersson    schedule 06.08.2013
comment
И поскольку наследование является общедоступным, C имеет не только A и B, но и является и A, и B. - person juanchopanza; 06.08.2013
comment
Приватные биты всегда вызывают смех, но friendship дает доступ ко всем битам, общедоступным и закрытым (если бы не было protected, не стоило бы говорить об этом). - person juanchopanza; 06.08.2013
comment
@juanchopanza: расширен, чтобы охватить защищенный dpublic. - person Mats Petersson; 06.08.2013

Есть несколько больших различий. Наследство и дружба очень разные.

При дружбе класс C НЕ является экземпляром класса A или класса B. Следовательно, если у вас есть такая функция, как:

void processMyClass(A* a);

вы не можете передать ему экземпляр C, тогда как, если C является подклассом A (публично), он ЯВЛЯЕТСЯ экземпляром A.

С помощью дружбы классы A и B могут касаться всех частных данных и функций членов C. С помощью наследования класс C может касаться общедоступных и защищенных членов A и B.

Дружба не передается по наследству. Это означает, например:

class D : public C
{
private:
   void foo() {
      // A and B cannot call this function
   }
}
person sbaker    schedule 06.08.2013

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

person Mike Weir    schedule 06.08.2013
comment
Я не думаю, что вам нужна кастинговая квалификация. И друзья также могут получить доступ к личным данным, а не только к защищенным данным. - person sbaker; 06.08.2013

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

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

Основное использование friendship в C++ — определение чего-то, что является частью интерфейса, но по синтаксическим причинам не может быть функцией-членом. Одним чрезвычайно распространенным примером является оператор вставки или извлечения потока. Чтобы реализовать их как функции-члены, они должны быть членами класса потока. Поскольку мы не хотим постоянно изменять класс потока, вместо этого они являются бесплатными функциями, которые принимают ссылку на поток в качестве левого параметра и ссылку на (возможно, константный) объект того типа, который они вставляют/извлекают в качестве своего левого параметра. правый операнд.

Они не должны обязательно быть friends класса — они могут быть написаны так, чтобы использовать только открытый интерфейс класса. Однако если вы это сделаете, это, как правило, означает, что класс выставляет в своем публичном интерфейсе больше, чем необходимо в противном случае. Интерфейс больше не минимален, что указывает на проблемный дизайн.

Однако одно замечание: вы можете определить дружественную функцию внутри определения класса:

class Foo { 
// ...
    friend std::ostream &operator<<(std::ostream &os, Foo const &f) { 
        // ...
    }
};

Поначалу это может показаться странным (да и синтаксически так оно и есть). Несмотря на то, что она определена внутри определения класса, friend означает, что это не функция-член. По крайней мере, на мой взгляд, это довольно точно отражает ситуацию: концептуально это часть класса. Он имеет доступ к закрытым членам, как и любой другой член класса. Тот факт, что это бесплатная функция, а не функция-член, является чисто артефактом реализации, который по сути не имеет ничего общего с дизайном кода.

Это также указывает на другое различие между friendship и наследованием: при наследовании вы обычно имеете дело в основном с функциями-членами. Каждая функция-член по-прежнему получает указатель this, поэтому каждая функция-член напрямую связана с конкретным экземпляром класса. Да, вы можете определить его так, чтобы он также получал (указатель или ссылку на) другой экземпляр класса, если это необходимо, но он всегда получает this независимо от этого. Друг (функция или класс) этого не понимает — объявление friend просто означает, что имена, закрытые для этого другого класса, видны friend. Чтобы получить доступ к фактическому экземпляру этого класса, вам обычно нужно передать его в качестве параметра или что-то в этом роде.

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

Защищенное наследование в значительной степени является ошибкой. Это разрешено, потому что это согласуется с членами public, private и protected (что имеет смысл), но для наследования protected просто не делает ничего полезного.

person Jerry Coffin    schedule 10.05.2015