У меня есть класс, который содержит некоторые функции (не виртуальные), и еще 2 класса публично наследуют этот класс. В обоих подклассах я переопределяю одну и ту же функцию базового класса.
После создания объектов всех трех классов в main (расположенных в одном и том же файле) я вызываю исходную функцию с объектом baseclass и переопределенные функции с объектами производного класса.
Я ожидал, что все 3 вызова функции запустят исходную функцию из базового класса (так как я нигде не использовал «виртуальный» в коде), но я фактически заставляю каждую версию этой функции работать в соответствии с классом, в котором она была определены (3 разных варианта).
У меня есть классы Base & Получается следующим образом:
struct Base
{
void foo();
};
struct Derived : Base
{
void foo();
};
в основном:
int main()
{
Derived d;
d.foo();
}
Я думал, что d.foo () должен запускать Base :: foo (), если не используется ‘virtual’.
Это не «переопределение» … и это не должно быть.
struct Base
{
void foo();
};
struct Derived : Base
{
void foo();
};
int main()
{
Derived d;
d.foo();
}
Если я вас правильно понимаю, то вы ожидали, что это исполнится Base::foo()
потому что функции не являются виртуальными и, следовательно, одна не переопределяет другую.
Но здесь вы не нужна виртуальная отправка: правила наследования просто утверждают, что вы получите правильную функцию для типа объекта, на котором вы ее запускаете.
Когда вам нужна виртуальная диспетчеризация / переопределение, это немного другой случай: это когда вы используете косвенность:
int main()
{
Base* ptr = new Derived();
ptr->foo();
delete ptr;
}
В приведенном фрагменте результат будет Base::foo()
называется, потому что выражение ptr->foo()
не знает что *ptr
является действительно Derived
, Все, что он знает, это то, что ptr
это Base*
,
Это где добавление virtual
(и при этом сделать одну функцию переопределение другой) заставляет волшебство случиться.
Вы не можете переопределить то, что не является виртуальным. Не виртуальные функции-члены отправляются статически в зависимости от типа объекта экземпляра.
Вы можете обмануть, «переопределив» функцию, сделав ее встроенной функцией, вызывающей что-то косвенно. Что-то вроде (в C ++ 03)
class Foo;
typedef int foo_sig_t (Foo&, std::string&);
class Foo {
foo_sig_t *funptr;
public:
int do_fun(std::string&s) { return funptr(*this,s); }
Foo (foo_sig_t* fun): funptr(fun) {};
~Foo () { funptr= NULL; };
// etc
};
class Bar : public Foo {
static int barfun(Bar&, std::string& s) {
std::cout << s << std::endl;
return (int) s.size();
};
public:
Bar () : Foo(reinterpret_cast<foo_sig_t*>)(&barfun)) {};
// etc...
};
и позже:
Bar b;
int x=b.do_fun("hello");
Официально это не перегружает виртуальную функцию, но выглядит очень близко к ней. Однако по моему выше Foo
пример каждого Foo
экземпляр имеет свой funptr
, который не обязательно является общим для класса. Но все Bar
экземпляры разделяют так же funptr
указывая на то же самое barfun
,
Кстати, используя C ++ 11 лямбда анонимные функции (внутренне реализовано как укупорочные), это было бы проще и короче.
Конечно, виртуальные функции в общем случае фактически реализуются с помощью аналогичного механизма: объекты (с некоторыми virtual
материал) неявно начать со скрытого поля (возможно, «по имени» _vptr
) давая виртуальные таблицы (или таблица виртуальных методов).