2013-08-24 3 views
0

Я немного запутался в следующих ситуациях: являются ли они правильными способами переопределения функции copy, но не перегружают, или все они правы?О переопределении виртуальных функций в C++

class Base{ 
public: 
    virtual Base* copy(Base* b){...} 
}; 

class Derived:public Base{ 
public: 
    virtual Base* copy(Base* b){...}//I know this should work 
// but how about the followings? 
    //virtual Base* copy(Derived* b){...} 
    //virtual Derived* copy(Base* b){...} 
    //virtual Derived* copy(Derived* b){...} 
}; 

КБТО, имеет значение изменение права доступа, имеет значение? скажем, я пишу Производный класс вроде этого:

class Derived:public Base{ 
private://or protected: 
    virtual Base* copy(Base* b){...} 
    ... 
}; 
+0

Я не понимаю ваш вопрос. Вы можете перефразировать его? Вы спрашиваете о правилах для _overriding_ виртуальных функций? –

+0

да, я перефразирую его сейчас. – Ghostblade

ответ

3

Они все правовые декларации, это просто, что эти два

virtual Base* copy(Derived* b); 
virtual Derived* copy(Derived* b); 

не переопределять copy из базового класса, так как их подпись отличается. Они просто объявляют новый виртуальный copy, который скрывает его от базы.
Это один, однако

virtual Derived* copy(Base* b); 

делает переопределение. У него такая же подпись и covariant return type.

В C++ 11 вы можете использовать override, чтобы заставить компилятор генерировать ошибку, если функция ничего не отменяет:

virtual Derived* copy(Derived*) override { /*... */} // will produce an error 

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

class Base { 
public: 
    virtual Base* copy(Base* b); 
}; 

class Derived : public Base { 
private: 
    virtual Base* copy(Base* b);  // Overrides Base::copy 
}; 

int main() 
{ 
    Base* b = new Derived; 
    Base* b2; 
    b->copy(b2); // calls Derived::copy 
    Derived d; 
    d.copy(b2); // error, as expected 
} 
6

Таковы правила переопределения функции:

[C++11: 10.3/2]: Если виртуальная функция-члена vf объявляется в классе Base и в классе Derived, полученным прямо или косвенно от Base, объявлена ​​функция-член vf с с тем же именем, списком параметров (8.3.5), cv-квалификацией и ref-qualifier (или отсутствием такого же), что и Base::vf, затем Derived::vf также является виртуальным (независимо от того, объявлено ли оно) или нет. переопределяетBase::vf. [..]

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

Итак:

class Base 
{ 
public: 
    virtual Base* copy(Base* b); 
}; 

class Derived : public Base 
{ 
public: 
    // Overrides Base::copy 
    virtual Base* copy(Base* b); 

    // Does NOT override Base::copy (due to different parameter-type-list) 
    virtual Base* copy(Derived* b); 

    // Overrides Base::copy (despite different return type) 
    virtual Derived* copy(Base* b); 

    // Does NOT override Base::copy (due to different parameter-type-list) 
    virtual Derived* copy(Derived* b); 

private: 
    // Overrides Base::copy (despite different access specifier) 
    virtual Base* copy(Base* b); 
}; 

Хотя, обратите внимание, что указанный класс Derived на самом деле плохо формируется, из-за конца 10.3/2 который гласит:

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

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

Это может быть удивительно, что virtual Derived* copy(Base* b) переопределяет Base::copy, так как он имеет другой тип возврата; это допускается при условии, что два типа возвращаемых covariant:

[C++11: 10.3/7]:Возвращаемого типа доминирующей функции должен быть идентичен типом возвращаемого перегруженной функции или ковариантным с классами функции. Если функция D::f отменяет функцию B::f, типы возврата функций ковариантны, если они удовлетворяют следующие критерии:

  • как указатели на классы, и является Lvalue ссылки на классы, или оба RValue ссылки классы
  • класса в типе возвращаемого B::f тот же класс, как класс в типе возвращаемого D::f, или являются однозначным и доступным прямым или косвенным базовым классом класса в типе возвращаемого D::f
  • как указатели или ссылки имеют один и тот же cv-qu а тип класса в возвращаемом типе D::f имеет ту же квалификацию cv как или менее cv-квалификацию, чем тип класса в возвращаемом типе B::f.

Что касается public против private вопроса о, нет никакого правила о том, что это имеет значение; ситуация осветляла сноской 111 в случае, если не было какие-либо сомнения:

функции с тем же именем, но с другим списком параметров (раздел 13) в качестве виртуальной функции не обязательно виртуальный и не отменяет , Использование спецификатора virtual в объявлении функции переопределения является законным, но избыточным (имеет пустую семантику). Контроль доступа (раздел 11) не учитывается при определении переопределения.

+1

+1 для «более одного перехватчика неформализирован». – jrok

+0

Вы не можете, однако, иметь _both_ 'Derived * copy (Base * b)' и 'Base * copy (Base * b)' в 'Derived'. Ковариация не означает, что вы можете иметь полиморфизм на основе типа возврата. –

+0

@ dan.p: Мой ответ уже объясняет, что разрешен только один последний переопределитель. Вы правы, конечно, конечно. –

0

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

Прежде всего, это немного неясно, когда метод copy является частью объекта, принимает объект как вход и возвращает объект. Копирует ли он с или на вход? Возвращает ли она копию или себя? Предполагается, что это static?

Все ваши декларации «работают» (в зависимости от того, чего вы хотите достичь), но не все вместе.

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

Для реализации только использования в Derived, вы можете объявить

class Derived:public Base{ 
public: 
    virtual Derived* copy(Base* b){...}; 
    virtual Derived* copy(Derived* b){}; 
}; 

или

class Derived:public Base{ 
public: 
    virtual Base* copy(Base* b){...}; 
    virtual Derived* copy(Derived* b){}; 
}; 

полиморфизм на основе возвратного типа не поддерживается в C++, однако. Вы не можно использовать

class Derived:public Base{ 
public: 
    virtual Base* copy(Derived* b){...}; 
    virtual Derived* copy(Derived* b){}; 
}; 

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

Derived * d = new Derived(); 

Derived * toCopy = new Derived(); 

Base * b2 = toCopy->copy(d); // Should use use the version returning Base 

Derived * d2 = toCopy->copy(d); // Should use the version returning Derived 

toCopy->copy(d2); // Which implementation should the compiler pick? It cannot know! 

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

Что касается права доступа, я с радостью рекомендую другие ответы.

+0

У вас есть две ошибки: 'Derived toCopyInto = new Derived();' и 'Derived toCopy = new Derived();' И перегрузка по типу возврата является незаконной для любой функции, будь то виртуальная или нет. Также 'Base b2 = toCopy.copy (d);' также является незаконным. – jrok

+0

@jrok, спасибо за указание на ошибки. То, что перегрузка по типу возврата является незаконным, - это то, что я пытался объяснить. Я попытался уточнить. –

+0

@Ghostblade Вы можете вызывать 'toCopyInto -> (Base *)' на 'Derived'. Это 'Base :: copy', а не' Derived :: copy', который будет вызываться. Потому что 'Base: copy (Base *)' в этом случае не переопределяется _not_. –

Смежные вопросы