Использование чисто виртуальных функций в C++?
Я узнаю о C++ в классе прямо сейчас, и я не совсем grok чистые виртуальные функции. Я понимаю, что они позже описаны в производном классе, но почему вы хотите объявить его равным 0, если вы просто собираетесь определить его в производном классе?
8 ответов
Короче говоря, это сделать класс абстрактным, чтобы его нельзя было создать, но дочерний класс может переопределить чистые виртуальные методы для формирования конкретного класса. Это хороший способ определить интерфейс в C++.
любой класс, содержащий чисто виртуальный метод абстрактный, то есть, он не может быть инстанцирован. Абстрактные классы полезны для определения некоторого основного поведения, которое подклассы должны совместно использовать, но позволяя (фактически, требуя) подклассы реализовать абстрактное индивидуально.
пример абстрактного класса:
class Foo {
// pure virtual, must be implemented by subclasses
virtual public void myMethod() = 0;
// normal method, will be available to all subclasses,
// but *can* be overridden
virtual public void myOtherMethod();
};
класс, в котором каждый метод является абстрактным, может использоваться в качестве интерфейса, требуя, чтобы все подклассы соответствовали интерфейсу путем реализации все методы, содержащиеся в нем.
пример интерфейса:
class Bar {
// all method are pure virtual; subclasses must implement
// all of them
virtual public void myMethod() = 0;
virtual public void myOtherMethod() = 0;
};
чистые виртуальные методы в C++ - это в основном способ определить интерфейсы без необходимости их реализации.
представьте, что я хочу смоделировать несколько видов форм, и все они имеют четко определенную область. Я решаю, что каждая форма должна наследовать IShape
("I" для интерфейса) и IShape
включает GetArea()
способ:
class IShape {
virtual int GetArea();
};
теперь проблема: как я должен вычислить площадь фигуры, если эта форма не переопределяет GetArea()
? То есть, какова наилучшая реализация по умолчанию? Круги используют pi * radius^2, квадраты используют length^2, параллелограммы и прямоугольники используют base * height, треугольники используют Основание 1/2 * высота, ромбы, пятиугольники, восьмиугольники, etc. используйте другие формулы.
поэтому я говорю: "если вы форма, вы должны определить способ расчета площади, но будь я проклят, если знаю, что это будет", определив метод pure virtual:
class IShape {
virtual int GetArea() = 0;
};
чтобы добавить к ответу Стивена Судита:
"Короче говоря, это сделать класс абстрактным, чтобы его нельзя было создать, но дочерний класс может переопределить чистые виртуальные методы для формирования конкретного класса. Это хороший способ определить интерфейс в C++."
примером этого может быть, если у вас есть базовый класс (возможно, Shape), который вы используете для определения ряда функций-членов, которые могут использовать его производные классы, но хотите предотвратить объявление экземпляра Shape и заставить пользователей использовать только производные классы (которые могут быть, прямоугольник, треугольник, пятиугольник и так далее)
RE: ответ Джеффа выше
не-абстрактные классы могут содержать виртуальные функции-члены и быть инстанцирован. Фактически, для перегрузки функций-членов это необходимо, поскольку по умолчанию C++ не определяет тип среды выполнения переменной, но при определении с помощью виртуального ключевого слова он будет.
рассмотрим этот код (Примечание, аксессоры, мутаторы, конструкторы, etc не включены для ясности):
class Person{
int age;
public:
virtual void print(){
cout << age <<endl;
}
}
class Student: public Person{
int studentID
public:
void print(){
cout << age << studentID <<endl;
}
}
теперь при запуске этот код:
Person p = new Student();
p.print();
без виртуального ключевого слова будет напечатан только возраст, а не возраст и studentID, как это должно произойти для класса Student
(этот пример основан на очень похожем примере c++ для java-программистов http://www.amazon.com/Java-Programmers-Mark-Allen-Weiss/dp/013919424X)
@Steven Sudit: вы полностью правильно, я забыл включить фактическое наследство, doh! Аксессоры и т. д. не включены, чтобы держать вещи яснее, и я сделал это более очевидным сейчас. 3-7-09: все основные
по существу, чистые виртуалы используются для создания интерфейса (аналогичного java). Это может использоваться как соглашение между двумя модулями (или классами, или что-то еще) относительно того, какую функциональность ожидать, без необходимости знать что-либо о реализации другой части. Это позволяет легко подключать и воспроизводить части, используя тот же интерфейс, без необходимости изменять что-либо в другом модуле, который использует ваш интерфейс.
для пример:
class IStudent
{
public:
virtual ~IStudent(){};
virtual std::string getName() = 0;
};
class Student : public IStudent
{
public:
std::string name;
std::string getName() { return name; };
void setName(std::string in) { name = in; };
};
class School
{
public:
void sendStudentToDetention(IStudent *in) {
cout << "The student sent to detention is: ";
cout << in->getName() << endl;
};
};
int main()
{
Student student;
student.setName("Dave");
School school;
school.sendStudentToDetention(&student);
return 0;
}
школе не нужно знать, как установить имя ученика, все, что ей нужно знать, это как получить имя ученика. Предоставляя интерфейс для реализации учащегося и школы для использования, существует соглашение между двумя частями о том, какая функциональность необходима школе для выполнения своей работы. Теперь мы можем переключаться в и из различных реализаций класса студента все, что мы хотим, не затрагивая школу (до тех пор, пока мы реализуем тот же интерфейс каждый раз.)
идея с абстрактными классами заключается в том, что вы все еще можете иметь переменную, объявленную с этим типом (т. е. это статический тип), но переменная фактически ссылается или указывает на фактический конкретный тип (динамический тип).
при вызове метода в C++ компилятор должен убедиться, что метод будет поддерживаться на этом объекте.
объявляя чистую виртуальную функцию, вы помещаете "заполнитель", который компилятор может использовать, чтобы сказать " oh... Я знаю, что все, на что ссылается эта переменная, будет принимать этот вызов", потому что фактические конкретные типы будут его реализовывать. Однако вам не нужно предоставлять реализацию в абстрактном типе.
Если вы ничего не объявили, то компилятор не будет иметь эффективного способа гарантировать, что он будет реализован всеми подтипами.
конечно, если вы спрашиваете, почему вы хотите сделать абстрактный класс, есть много информации об этом.