Итак, у меня есть эти заголовочные файлы:
Shape.h:
class Shape
{
public:
Shape();
virtual ~Shape();
virtual double getArea();
virtual void printDraw();
bool isLegal(Shape shape);
};
Trig.h:
class Trig : public Shape
{
public:
Trig(Point pointA, Point pointB, Point pointC);
virtual ~Trig();
virtual double getArea();//override
virtual void printDraw();//override
bool isLegal(Trig trig);//override
};
но когда я пытаюсь реализовать Trig.cpp, я получаю ошибки.
Я старался :
Trig::Trig(Point pointA, Point pointB, Point pointC) {..}
Я просматривал Интернет, но я, кажется, не делаю наследство должным образом.
[Мой код ДОЛЖЕН иметь заголовочный файл, который включает только объявления! … Требования назначения!]
Я новичок в использовании как наследования, так и виртуальных функций в C ++ [Я использовал наследование в Java]
А что касается виртуальных функций .. есть ли разница в их реализации, чем в обычных функциях? [Я понимаю разницу в поведении].
В комментарий Вы объясняете это
«Я получаю сообщение об ошибке: неопределенная ссылка на Shape :: Shape ()»
Это означает, что вы забыли предоставить реализацию этого конструктора.
Теперь, когда техническая проблема решена, посмотрите на дизайн.
Первые два метода страдают от двух недугов: (1) хотя они никогда не должны мутировать объект, они не объявлены const
поэтому их нельзя вызывать на const
объект и (2) get
Префикс снижает читабельность, больше печатать и не имеет общего преимущества. Я нашел get
Префикс полезен в некоторых редких случаях в качестве устройства для устранения неоднозначности, но все случаи использования, которые я видел новичков, были неадекватным копированием соглашения Java, что имеет смысл в Java, но не в C ++. Итак, вместо …
virtual double getArea();
virtual void printDraw();
делать
virtual double area() const;
virtual void print() const;
Затем метод …
bool isLegal(Shape shape);
неправильно во многих отношениях … Ой! Но начнем с чисто технического.
С чисто технической точки зрения излишне неэффективно передавать аргумент по значению, что влечет за собой операцию копирования для каждого вызова. Вместо этого передайте объект по ссылке. И сделать это ссылку на const
, чтобы поддержать const
объекты и rvalue объекты в качестве аргументов:
bool isLegal(Shape const& shape);
Далее, назвав: isLegal
это плохое имя, потому что большинство любых объектов C ++ будут легальными. Это должно было бы быть, скажем, порнографические и проживание в незападных стране, для того, чтобы стать незаконным. И я не могу за жизнь мне думать о каком-либо способе сделать объект порнографического или сделать его постоянно находиться в каком-то конкретном географическом регионе.
Так,
bool isValid(Shape const& shape);
Далее, низкий уровень дизайна, нет веских причин, чтобыvirtual
Метод как обычная функция-член, потому что для этого требуется, чтобы вы вызывали его для объекта. Но вся информация, в которой она нуждается, находится в обычном споре. Мы можем увидеть эту путаницу в производном классе, где …
bool isLegal(Trig trig);//override
это вовсе не переопределение: это технически перегрузка имени функции, что означает, просто другую функцию с тем же именем. Здесь нет виртуальности, нет переопределения. И это не нужно.
Итак, сделайте это static
функция-член, которую не нужно вызывать для объекта:
static bool isValid(Shape const& shape);
Наконец, более высокий уровень проектирования, весь механизм конструкторов и деструкторов C ++ существует, чтобы избежать таких методов и проверок.
Идея в том, что вы …
Установите действительный объект в каждом конструкторе.
Сохраняйте объект действительным в каждом методе.
Тогда объект просто не может стать недействительным. Этот подход называется однофазное строительство, и свойства объекта, который делает его «действительным», известны как класс инвариант класса. Который должен быть установлен каждым конструктором и поддерживаться каждым методом.
Это означает, что окончательная версия isValid
функция УСТРАНЕНУ него нет работы, потому что эта работа (должным образом) выполняется конструктором (-ами) и методами.
Хорошо, есть некоторые технические проблемы с однофазным построением, в частности, как сделать специфическую для производного класса инициализацию в конструкторе базового класса. Это покрыто C ++ FAQ. Часто полезно прочитать FAQ.
Первая версия верна:
Trig::Trig(Point pointA, Point pointB, Point pointC) {..}
Он скомпилируется, но не будет ссылаться. Причина в том, что у вас есть объявленный беспараметрический конструктор Shape
, но вы не смогли определять Это. Эйхер добавить определение
Shape::Shape() {..}
или если конструктор по умолчанию в порядке, удалите объявление из заголовка Shape.