Наследование в C ++: определение переменных в родительско-дочерних классах - PullRequest
4 голосов
/ 03 мая 2019

Задача

Я ищу лучший способ определения переменных в родительско-дочерних классах, чтобы их вызывал указатель на их родительский класс.
Это протокод:

class Base {
    public:
        virtual void function() = 0;
};

class A : public Base {
public:
    int a, b;
    A(int a_, int b_) : a(a_), b(b_) {};
    void function() { // do something.. }
};

class B : public Base {
public:
    int a, b;
    B(int a_, int b_) : a(a_), b(b_) {};
    void function() { // do something.. }
};

Base* elements[2] = {
    new A(1,2),
    new B(3,4)
};

Поскольку я определяю a, b в обоих конструкторах, я могу определить их в абстрактном классе Base. Таким образом, код должен быть более эффективным и чистым. Правильна ли эта практика? Как мне их определить?

Возможные решения

Решение, которое я имею в виду, заключается в реализации функции, которая возвращает, например, a, например:

class Base {
    public:
        virtual int return_a() = 0;
};

class A : public Base {
    public:
        int a, b;
        A(int a_, int b_) : a(a_), b(b_) {};
        int return_a() {
        return a;
        }
};

int main(){
    int a = elements[0]->return_a();
}

Это работает, но я уверен, что это не эффективный способ. Лучше определить a, b в абстрактном классе? Спасибо

Ответы [ 2 ]

1 голос
/ 03 мая 2019

Правильна ли эта практика?

Я думаю, что это превращается в основанный на мнении вопрос-ответ.Если все ваши производные классы должны включать членов a и b, , то, по моему мнению, , они должны быть частью базового класса.Таким образом, вам гарантировано, что все ваши производные классы будут включать членов a и b, и вы (или кто-то еще) не рискуете забыть включить их.Кроме того, добавляя члены в базовый класс, вы экономите память, не добавляя их в каждый производный класс.virtual в C ++ предоставляет вам все необходимые инструменты для достижения полиморфизма, что и происходит, когда вы создаете массив Base *.

. Я бы также рекомендовал использовать ключевое слово override для виртуальных функций.которые переопределяются в производном классе, и ключевое слово final для производных классов, которые не предназначены стать базовыми классами.Вы можете ознакомиться с преимуществами использования этих ключевых слов из книги Скотта Мейерса по современному C ++.

struct Base
{
    int a, b;

    Base(int a_, int b_) : a(a_) , b(b_)
    {;}

    virtual void function() = 0;
};

struct A : Base // A can be a base class of another class.
{
    A(int a_, int b_) : Base(a_,b_)
    {;}

    void funtion() // this will compile, but it's not going to override the Base::function()
    {;}
};

struct B final : Base // B can never become a base class.
{
    B(int a_, int b_) : Base(a_,b_)
    {;}

    void funtion() override // this won't compile because override will see that we mis-spelled function() 
    {;}
};

Однако не существует правила C ++, которое запрещало бы включать членов во все ваши производные классы.

Кроме того, если все ваши члены public, вы можете использовать struct, чтобы избежать необходимости вводить public внутри классов и в методе наследования.

struct Base
{
    // all members are public
};

struct Derived : Base // public inheritance by default.
{
    // all members are public
};
0 голосов
/ 03 мая 2019

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

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

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

Это одно из руководств по кодированию, данное Бьярном Страуструпом под названием: When designing a class hierarchy, distinguish between implementation inheritance and interface inheritance.

Причиной является:

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

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

...