Вопрос по виртуальным и стандартным аргументам - PullRequest
2 голосов
/ 21 июля 2011

Я хотел бы получить подтверждение о следующих вещах:

Виртуальный механизм:

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

class A
{
public: 
virtual void something();
}

class B:public A
{
public:
virtual void something();
}

Означает ли это, что мы хотим переопределить метод somethign в классах, производных от класса B?

Кроме того, еще один вопрос:

У меня есть класс A, производный от трех разных классов. Теперь в базовом классе A. есть виртуальный метод everything (),

Теперь, если бы мне нужно было добавить новый аргумент по умолчанию для этого метода в базовом классе, A :: что угодно (), мне нужно было бы добавить его также во все 3 класса.

Мой выбор ответов:

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

Пожалуйста, дайте мне знать, если то, что я чувствую (выше 2), является правильным.

Спасибо, ПаванMoanr.

Ответы [ 4 ]

2 голосов
/ 14 августа 2011

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

Этот вопрос хорошо освещен в этом вопросе: Является ли функция в C ++ автоматически виртуальной, если она переопределяет виртуальную функцию?


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

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

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

Поэтому, когда динамический и статический типы не совпадают, последуют неожиданные результаты.

, например

#include <iostream>

class A
{
public:
  virtual void foo(int n = 1) { std::cout << "A::foo(" << n << ")" << std::endl; }
};

class B : public A
{
public:
  virtual void foo(int n = 2) { std::cout << "B::foo(" << n << ")" << std::endl; }
};

int main()
{
  A a;
  B b;

  a.foo(); // prints "A::foo(1)";
  b.foo(); // prints "B::foo(2)";

  A& ref = b;
  ref.foo(); // prints "B::foo(1)";
}

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

class A
{
public:
   void defaultFoo() { foo(1); }
   virtual void foo(int n) { .... }
};

Если ваши переопределения имеют разные значения по умолчанию, у вас есть два варианта:

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

Я предпочитаю последнее.

0 голосов
/ 15 августа 2011

Как кто-то указал, функция в производном классе с тем же именем и сигнатурой типа, что и у виртуальной функции в базовом классе, автоматически всегда является виртуальной функцией.

Но ваш второй вопрос об аргументах по умолчанию:интересно.Вот инструмент для размышления над проблемой ...

class A {
  public:
   virtual void do_stuff_with_defaults(int a = 5, char foo = 'c');
};

почти эквивалентен этому:

class A {
  public:
   virtual void do_stuff_with_defaults(int a, char foo);

   void do_stuff_with_defaults() { // Note lack of virtual keyword
      do_stuff_with_defaults(5, 'c'); // Calls virtual function
   }

   void do_stuff_with_defaults(int a) { // Note lack of virtual keyword
      do_stuff_with_defaults(a, 'c'); // Calls virtual functions
   }
};

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

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

0 голосов
/ 15 августа 2011

Вот небольшой эксперимент для проверки того, что вы хотите знать:

class A {
public:
        virtual void func( const char* arg = "A's default arg" ) {
                cout << "A::func( " << arg << " )" << endl;
        }
};

class B : public A {
public:
        void func( const char* arg = "B's default arg" ) {
                cout << "B::func( " << arg << " )" << endl;
        }
};

class C : public B {
public:
        void func( const char* arg ) {
                cout << "C::func( " << arg << " )" << endl;
        }
};

int main(int argc, char* argv[])
{
        B* b = new B();
        A* b2 = b;
        A* c = new C();
        b->func();
        b2->func();
        c->func();
        return 0;
}

результат:

B::func( B's default arg )
B::func( A's default arg )
C::func( A's default arg )

вывод:

1- virtualКлючевое слово перед объявлением func A делает эту функцию виртуальной в B и C.

2- Используемый по умолчанию аргумент - это тот, который объявлен в классе указателя / ссылки, который вы используете для доступа к объекту.

0 голосов
/ 14 августа 2011

Неважно, пишете ли вы virtual в производном классе или нет, оно всегда будет виртуальным из-за базового класса, однако все же лучше включить virtual, чтобы явно указать, что оно виртуальное, а затем если вы случайно удалите это ключевое слово из базового класса, это вызовет ошибку компилятора (вы не можете переопределить не виртуальную функцию виртуальной). РЕДАКТИРОВАТЬ >> извините, я был не прав. Вы можете переопределить не виртуальную функцию с помощью виртуальной, однако, как только она станет виртуальной, функции всех производных классов с одинаковой сигнатурой также будут виртуальными, даже если вы не пишете ключевое слово virtual. << </p>

Если вы не переопределите виртуальную функцию, будет использовано определение из базового класса (как если бы оно было скопировано дословно).

Если вы хотите указать, что виртуальная функция должна быть переопределена в классе dervied, вам не следует предоставлять какую-либо реализацию, т.е. virtual void something() = 0; В этом случае ваш класс будет абстрактным базовым классом (ABC), и никакие объекты не могут быть созданы из него. Если производный класс не предоставляет свой собственный реализатор, он также будет ABC.

Я не уверен, что вы имеете в виду для аргументов по умолчанию, но сигнатуры функций должны совпадать, поэтому все параметры и возвращаемые значения должны быть одинаковыми (лучше не смешивать аргументы перегрузки / по умолчанию с наследованием, потому что вы можете получить очень неожиданные результаты для пример:

class A
{
public: 
void f(int x);
};

class B:public A
{
public:
void f(float x);
};

int main() {
B b;
b.f(42); //this will call B::f(float) even though 42 is int
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...