Цели интерфейсов: Создать свободно связанное программное обеспечение [дублировать]

Компиляция программы на C ++ выполняется в несколько этапов, как указано в 2.2 (кредиты для Кейта Томпсона для ссылки) :

Превалирование среди правил синтаксиса

  1. Физические символы исходного файла сопоставляются в соответствии с реализацией в соответствии с базовым набором символов источника (ввод символов новой строки для индикаторов конца строки) при необходимости. [SNIP]
  2. Каждый экземпляр символа обратной косой черты (\), за которым сразу следует символ новой строки, удаляется, сплайсируя физические исходные строки для формирования логических строк источника. [SNIP]
  3. Исходный файл разбивается на токены предварительной обработки (2.5) и последовательности символов пробела (включая комментарии). [SNIP]
  4. Выполнены предпроцессорные директивы, макро-вызовы разворачиваются и выполняются операторные выражения _Pragma. [SNIP]
  5. Каждый элемент набора символов в символьном литерале или строковый литерал, а также каждая escape-последовательность и универсальное имя-символа в символьном литерале или не- -raw строковый литерал, преобразуется в соответствующий член набора символов выполнения; [SNIP]
  6. Соединительные маркеры литералов строки объединены.
  7. Символы пробела, разделяющие токены, уже не являются значимыми. Каждый токен предварительной обработки преобразуется в токен. (2.7). Результирующие маркеры синтаксически и семантически анализируются и переводятся как единица перевода. [SNIP]
  8. Устанавливаемые единицы перевода и единицы экземпляра объединяются следующим образом: [SNIP]
  9. Все ссылки на внешние сущности решена. Компоненты библиотеки связаны для удовлетворения внешних ссылок на объекты, не определенные в текущем переводе. Весь такой переводчик выводится в образ программы, который содержит информацию, необходимую для выполнения в среде выполнения. (акцент мой)

[footnote] Реализации должны вести себя так, как если бы эти отдельные фазы происходили, хотя на практике различные фазы могли быть свернуты вместе.

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

Скажите, что вы определили символ a в a.cpp. Теперь b.cpp объявил этот символ и использовал его. Перед связыванием он просто предполагает, что этот символ был определен где-то , но он пока не заботится о том, где. Фаза связывания отвечает за поиск символа и правильную привязку его к b.cpp (ну, собственно, к объекту или библиотеке, которая его использует).

Если вы используете Microsoft Visual Studio, вы будете см., что проекты генерируют файлы .lib. Они содержат таблицу экспортированных символов и таблицу импортированных символов. Импортированные символы разрешены против библиотек, на которые вы ссылаетесь, и экспортированные символы предоставляются для библиотек, которые используют этот .lib (если есть).

Подобные механизмы существуют для других компиляторов / платформ.

Общие сообщения об ошибках: error LNK2001, error LNK1120, error LNK2019 для Microsoft Visual Studio и undefined reference to symbolName для GCC.

Код:

struct X
{
   virtual void foo();
};
struct Y : X
{
   void foo() {}
};
struct A
{
   virtual ~A() = 0;
};
struct B: A
{
   virtual ~B(){}
};
extern int x;
void foo();
int main()
{
   x = 0;
   foo();
   Y y;
   B b;
}

генерирует следующие ошибки с GCC:

/home/AbiSfw/ccvvuHoX.o: In function `main':
prog.cpp:(.text+0x10): undefined reference to `x'
prog.cpp:(.text+0x19): undefined reference to `foo()'
prog.cpp:(.text+0x2d): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD1Ev[B::~B()]+0xb): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD0Ev[B::~B()]+0x12): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1Y[typeinfo for Y]+0x8): undefined reference to `typeinfo for X'
/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1B[typeinfo for B]+0x8): undefined reference to `typeinfo for A'
collect2: ld returned 1 exit status

и аналогичные ошибки с Microsoft Visual Studio:

1>test2.obj : error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)
1>test2.obj : error LNK2001: unresolved external symbol "int x" (?x@@3HA)
1>test2.obj : error LNK2001: unresolved external symbol "public: virtual __thiscall A::~A(void)" (??1A@@UAE@XZ)
1>test2.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall X::foo(void)" (?foo@X@@UAEXXZ)
1>...\test2.exe : fatal error LNK1120: 4 unresolved externals

. Общие причины включают в себя:

9
задан Appulus 7 November 2013 в 14:14
поделиться

15 ответов

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

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

16
ответ дан cletus 4 September 2018 в 06:53
поделиться

При проектировании классов существует две вещи:

  1. Поведение объекта.
  2. реализация объекта.

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

Давайте рассмотрим сценарий развития, когда вещи (запрос, модель класса и т. Д.) Меняются очень часто, и вам нужно доставить определенные версии приложений.

Первоначальная постановка задачи: вам нужно создать класс «Поезд» для индийской железной дороги, который имеет поведение maxSpeed ​​в 1970 году.

1. Бизнес-моделирование с абстрактным классом

V 0.0 (начальная проблема) Исходная постановка задачи: вам нужно создать класс Train для индийской железной дороги, который имеет поведение maxSpeed ​​в 1970 году.

public abstract class Train {
    public int maxSpeed();
}

V 1.0 (Измененная проблема 1) Измененная постановка задачи: вам нужно создать класс Diesel Train для индийской железной дороги, который имеет поведение maxSpeed ​​в 1975 году.

public abstract class DieselTrain extends train {
     public int maxFuelCapacity ();
}

V 2.0 (Измененная проблема 2 ) chanded problem: вам нужно создать класс ElectricalTrain для индийской железной дороги, который имеет поведение maxSpeed, maxVoltage в 1980 году.

public abstract class ElectricalTrain extends train {
     public int maxvoltage ();
}

V 3.0 (Измененная проблема 3)

: вам нужно создать класс HybridTrain (используется как дизель, так и электромобиль) для индийской железной дороги, который имеет поведение maxSpeed, maxVoltage, maxVoltage в 1985 году.

public abstract class HybridTrain extends ElectricalTrain , DisealTrain {
    { Not possible in java }
}
{here Business modeling with abstract class fails}

2. Бизнес-моделирование с интерфейсом

Просто измените слово abstract на interface и ... ваше бизнес-моделирование с интерфейсом будет успешным.

http: //javaqna.wordpress. ком / 2008/08/24 / почему-использование-на-интерфейсам-вместо-о-абстрактных классов-это-поощряются-в-Java-программирование /

3
ответ дан janisz 4 September 2018 в 06:53
поделиться

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

Базовый класс предлагает возможность построить дерево наследования, где более сложные классы (из общий «тип») построены на основе более простых «базовых» классов. Классический и раздражающий пример в OO обычно является базовым классом «Shape», который наследуется Triangle, Square и т. Д.

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

In ваш пример выше, у меня будет объект «MyObjectGroup», наследующий базовый класс «MyObject», и ничего не получается из интерфейса, который я вижу.

4
ответ дан Lazarus 4 September 2018 в 06:53
поделиться

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

Я бы не использовал IXmlSerializable если бы не пришлось; это грязный, хитрый интерфейс, который часто является причиной горения.

В чем именно ваши требования к сериализации? Могут быть лучшие варианты ... однако для многих сериализаторов базовый класс будет проще, чем интерфейс. Например, для XmlSerializer вы могли бы:

[XmlInclude(typeof(MyObject))] // : ObjectBase 
[XmlInclude(typeof(MyObjectGroup))] // : ObjectBase 
public abstract class ObjectBase { /*  */ }

(точный подход зависит от сериализатора)

7
ответ дан Marc Gravell 4 September 2018 в 06:53
поделиться

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

1
ответ дан Marcin Deptuła 4 September 2018 в 06:53
поделиться

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

Например, IXmlSerializable не является «сущностью» сам по себе. Он определяет контракт, который может реализовать предприятие. Интерфейсы живут «вне» иерархии наследования.

5
ответ дан Mehrdad Afshari 4 September 2018 в 06:53
поделиться

Вы действительно можете пойти с ОБО. ObjectBase избавляет вас от необходимости реализовывать общие свойства более одного раза и реализует IObject для вас. Везде, где вы его используете, обратитесь к IObject, чтобы вы могли провести тестирование с помощью mocks позже

1
ответ дан n8wrl 4 September 2018 в 06:53
поделиться

Это некоторые различия между интерфейсами и абстрактными классами.

1A. Класс может наследовать (реализовать) один или несколько интерфейсов. Таким образом, в C # интерфейсы используются для достижения множественного наследования. 1В. Класс может наследовать только один абстрактный класс.

2A. Интерфейс не может предоставить какой-либо код, а только подпись. 2B. Абстрактный класс может предоставить полный код по умолчанию и / или только детали, которые необходимо переопределить.

3A. Интерфейс не может иметь модификаторы доступа для подсистем, функций, свойств и т. Д. Все считается общедоступным. 3B. Абстрактный класс может содержать модификаторы доступа для подтипов, функций, свойств.

4A. Интерфейсы используются для определения периферийных способностей класса. Напр. A Ship и Car могут реализовать интерфейс IMovable. 4В. Абстрактный класс определяет идентификатор ядра класса и там он используется для объектов.

5A. Если в разных реализациях используются только сигнатуры методов, лучше использовать интерфейсы. 5В. Если различные реализации имеют один и тот же вид и используют общее поведение или статус, тогда лучше использовать абстрактный класс.

6A. Если мы добавим новый метод в интерфейс, мы должны отследить все реализации интерфейса и определить реализацию для нового метода. 6В. Если мы добавим новый метод в абстрактный класс, то у нас есть возможность обеспечить реализацию по умолчанию, и поэтому весь существующий код может работать исправно.

7A. Интерфейс не может определять поля. 7В. Абстрактный класс может иметь поля и константы.

8A. Интерфейс не может иметь конструктор. 8В. Абстрактный класс может иметь встроенные конструкторы по умолчанию.

9A. Интерфейс может наследовать только от других интерфейсов. 9В. Абстрактный класс может наследовать от интерфейсов, абстрактного класса или даже класса.

10
ответ дан nawfal 4 September 2018 в 06:53
поделиться

Я использовал абстрактные классы в своих проектах, но в будущих проектах я буду использовать интерфейсы. Преимущество «множественного наследования» чрезвычайно полезно. Возможность всегда предлагать совершенно новую реализацию класса, как в коде, так и в целях тестирования, всегда приветствуется. Наконец, если в будущем вы захотите иметь возможность настраивать свой код внешними разработчиками, вам не нужно давать им свой реальный код - они могут просто использовать интерфейсы ...

0
ответ дан SaguiItay 4 September 2018 в 06:53
поделиться

Все остальные равны, идите с интерфейсом.

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

1
ответ дан Sterno 4 September 2018 в 06:53
поделиться

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

Например, если вы реализуете иерархию классов для транспортных средств, реализуйте интерфейс под названием Vehicle, обладающий такими свойствами, как Color MaxSpeed ​​и т. д., и такими методами, как Drive (). Все дочерние классы, такие как Car Scooter AirPlane SolarCar и т. Д., Должны основываться на этом базовом интерфейсе, но обеспечивать отдельную реализацию методов и свойств, предоставляемых Vehicle.

-> Если вы хотите, чтобы ваши дочерние классы реализовали несколько несвязанных функций в коротких интерфейсах с множественным наследованием.

Например, для если вы реализуете класс SpaceShip, который должен иметь функциональность с транспортного средства, а также из НЛО, то сделайте как Транспортное средство, так и НЛО в качестве интерфейсов, а затем создайте класс SpaceShip, который реализует как Vehicle, так и UFO.

Абстрактные классы:

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

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

-> Цель абстрактного класс должен предоставить общее определение базового класса, к которому могут делиться несколько производных классов.

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

2
ответ дан user3824027 4 September 2018 в 06:53
поделиться

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

Абстрактные классы позволяют определить некоторые поведения; они заставляют ваши подклассы предоставлять другие. Например, если у вас есть инфраструктура приложения, абстрактный класс может предоставлять сервисы по умолчанию, такие как обработка событий и сообщений. Эти службы позволяют вашему приложению подключаться к инфраструктуре приложения. Однако есть некоторые функциональные возможности приложения, которые могут выполнять только ваше приложение. Такие функции могут включать задачи запуска и завершения работы, которые часто зависят от приложения. Поэтому вместо того, чтобы пытаться определить это поведение, абстрактный базовый класс может объявлять абстрактные методы остановки и запуска. Базовый класс знает, что ему нужны эти методы, но абстрактный класс позволяет вашему классу признать, что он не знает, как выполнять эти действия; он знает только, что он должен инициировать действия. Когда пришло время запуска, абстрактный класс может вызвать метод запуска. Когда базовый класс вызывает этот метод, Java вызывает метод, определенный дочерним классом.

Многие разработчики забывают, что класс, который определяет абстрактный метод, также может вызвать этот метод. Абстрактные классы - отличный способ создания запланированных иерархий наследования. Они также являются хорошим выбором для классов nonleaf в иерархиях классов.

0
ответ дан Chaitanya 4 September 2018 в 06:53
поделиться

Обратите внимание, что вы не можете переопределять операторы в интерфейсах. Это единственная реальная проблема с ними, насколько мне известно.

0
ответ дан David Rutten 4 September 2018 в 06:53
поделиться

Если у вас есть функция в классе, вы должны использовать абстрактный класс вместо интерфейса. В общем случае интерфейс используется от имени типа.

0
ответ дан Edwin Tai 4 September 2018 в 06:53
поделиться

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

Следовательно, с точки зрения использования & amp; философия, разница заключается в том, что, создавая абстрактный класс, вы ограничиваете любую другую функциональность, которую могут реализовать объекты этого класса, и предоставляют этим объектам некоторые базовые функции, общие для любого такого объекта (который является своего рода ограничением, тоже), а при настройке интерфейса вы не устанавливаете ограничений для других функций и не создаете никаких положений реального кода для этой функциональности, которые вы имеете в виду. Используйте абстрактные классы, когда вы узнаете все, что объекты этого класса должны делать в интересах своих пользователей. Используйте интерфейсы, когда объекты могут также делать что-то еще, о которых вы даже не можете догадаться.

0
ответ дан Evgeniy 4 September 2018 в 06:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: