Перейти к содержимому

Как создать массив абстрактного класса с

  • автор:

Не могу объявить массив указателей на класс, который наследуется об абстрактного класса

<code></p>
<p>При объявление массива указателей на это класс не могу присвоить им значения.вот скрин ошибок(работаю в xcode 8</code>» /></p>
<p>Отслеживать<br />
Gleb Nazemnov<br />
задан 20 мая 2017 в 17:32<br />
Gleb Nazemnov Gleb Nazemnov<br />
111 1 1 золотой знак 1 1 серебряный знак 9 9 бронзовых знаков</p>
<p>Так «не могу объявить», или «объявить могу, но не могу присвоить значения»? Почему в заголовке написано одно, а в тексте — совсем другое?</p>
<p>20 мая 2017 в 17:58</p>
<h3>2 ответа 2</h3>
<p>Сортировка: Сброс на вариант по умолчанию</p>
<p>Ваши классы-наследники реализуют не все чисто-виртуальные методы. Они либо должны реализовывать makeARezalt(), либо он должен быть просто виртуальным, либо вообще не виртуальным. Кстати, вы забыли про виртуальный деструктор в базовом классе и в наследниках, без него ваши объекты гарантированно будут неправильно удалены.</p>
<p>И если уж вы пишете на c++, используете std::vector> для массива указателей.</p>
<p>Edit: Вот вариант решения. Для std::make_unique нужен c++14, но можно создавать объекты просто через new.</p>
<pre><code>#include #include #include #include #include #include class UnaryFunction < public : virtual void show() const = 0; virtual double calcValue(double x) const = 0; virtual ~UnaryFunction() = default; >; class LineFunction : public UnaryFunction < double a, b; public : LineFunction(): a(0), b(0) <>LineFunction(double a, double b): a(a), b(b) <> void show() const override < std::coutdouble calcValue(double x) const override < return a*x+b; >~LineFunction() override = default; >; class CubeFunction : public UnaryFunction < double a, b,c; public : CubeFunction(): a(0), b(0), c(0) <>CubeFunction(double a, double b, double c): a(a), b(b), c(c) <> void show() const override < std::cout double calcValue(double x) const override < return a*x*x+b*x+c; >~CubeFunction() override = default; >; class HyperbolaFunction : public UnaryFunction < double a; public : HyperbolaFunction(): a(0) <>HyperbolaFunction(double a): a(a) <> void show() const override < std::coutdouble calcValue(double x) const override < if(x!=0)< return a/x; >else< throw std::invalid_argument; > > ~HyperbolaFunction() = default; >; int main(int argc, const char * argv[]) < volatile double x = 12; std::vector> functions; functions.push_back(std::make_unique(1, 2)); functions.push_back(std::make_unique(3)); functions.push_back(std::make_unique(15, 2, 4)); functions.push_back(std::make_unique(18, 12)); try< for(auto&& function: functions)< function->show(); std::cout f(x) mt24

)" data-controller="se-share-sheet" data-se-share-sheet-title="Поделиться ссылкой на ответ" data-se-share-sheet-subtitle="" data-se-share-sheet-post-type="answer" data-se-share-sheet-social="facebook twitter " data-se-share-sheet-location="2" data-se-share-sheet-license-url="https%3a%2f%2fcreativecommons.org%2flicenses%2fby-sa%2f3.0%2f" data-se-share-sheet-license-name="CC BY-SA 3.0" data-s-popover-placement="bottom-start">Поделиться
)" title="">Улучшить ответ
)">изменён 20 мая 2017 в 19:24
ответ дан 20 мая 2017 в 17:46
6
    Добавлю, что сигнатура Show должна быть одинаковой во всех реализациях
    – int3
    20 мая 2017 в 17:49
    Да, ошибку я заметил, функцию makeARezalt() переиминовал в Value как и в наследниках. Но это не помогло. А Show по заданию не может быть одинаковой, в этом и суть Show.
    – Gleb Nazemnov
    20 мая 2017 в 18:00
    @GlebNazemnov вы неправильно понимаете смысл виртуальных функций. Можно считать, что базовый класс содержит указатели на виртуальные функции, когда вы создаете наследников, этим указателям присваиваются специфичные для наследника значения. Но для этого сигнатура виртуальных функций в базовом классе и в наследниках должна совпадать. Конкретно в вашем случае в метод show и в метод value можно передавать массив значений (std::vector), так как количество аргументов меняется.
    – Ariox
    20 мая 2017 в 18:25
    @GlebNazemnov: Но можно сделать по-другому: пусть метод show вообще не принимает аргументы, а value принимает только x. А остальные значения пусть задаются в конструкторах классов, или специальными методами set_a, set_b и т.д., при этом эти методы не должны быть виртуальными, т.к. относятся к конкретному классу.
    – Ariox
    20 мая 2017 в 18:28
    1
    @ТимурМашарипов , т.к. классы полиморфные, их нужно хранить по указателю, т.к. их размер неизвестен. Чтобы не забыть удалить объект, оборачиваем указатели в unique_ptr, который удалит объект при своем собственном удалении (что, в свою очередь, произойдет при удалении вектора). unique_ptr особенно полезен при возникновении исключений. С вектором, думаю, понятно - количество аргументов функции может быть разным.
    – Ariox
    16 мая 2020 в 15:24
|Показать ещё 1 комментарий
1

Ваши классы остаются абстрактными, ибо вы не реализуете чисто виртуальные функции:

class Function < public : virtual void Show() = 0; virtual double Value() = 0; >;

У вас нет реализаций double Value(); , вы передаете в них параметры - значит, сигнатуры разные, так что.

Я бы на вашем месте делал

class Function < public : virtual double Value(double x) const = 0; virtual ~Function()<>>; 

Ну, а наследники должны реализовывать соответствующую функцию, типа

class Cube : public Function < double a, b,c; public: Cube(double a = 0.0, double b = 0.0, double c = 0.0): a(a), b(b), c(c) <>double Value(double x) const < return (a*x+b)*x+c; >~Cube()<> >; 

Ну вот на фига вы передавали в Value() значения a , b и c . Если вы их храните в классе?

Что будет содержать абстрактный класс?

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

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

А вот реализация статического массива вообще как-то не вяжется с остальными. На сколько эта часть задания соответствует здравому смыслу и реальным задачам? На мой не опытный взгляд уже есть обычный встроенный массив и в реализации динамического массива его следует использовать как поле данных. Как реализовать свой статический массив я не представляю.

  • Вопрос задан более трёх лет назад
  • 382 просмотра

1 комментарий

Оценить 1 комментарий

AtomKrieg

Сравните std::vector и std::array
Решения вопроса 1
Живу в своё удовольствие

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

Для реализации статического массива в конструкторе выделяйте память на нужное количество элементов, а при доступе по индексу - просто делайте смещение от указателя на первый элемент - вот вам и реализация стандартного массива

Ответ написан более трёх лет назад
Комментировать
Нравится Комментировать
Ответы на вопрос 2
Full-stack developer (Symfony, Angular)

вы можете организовать одинаковый интерфейс для связанного списка и статического массива? Можете. Вот и весь ответ.

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

Ответ написан более трёх лет назад
vipermagi @vipermagi Автор вопроса

Вы намекаете, что реализация пользовательского статического массива это просто обёртка над встроенным статическим массивом?

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

vipermagi: ну у вас же будет операция вставки в середину списка например? Со статическим массивом вам нужно будет хэндлить ресайзы и т.д. Интерфейс один, реализация разная.

Программист на «си с крестами» и не только

Вариант один вы предложили. Всё пишу на C++03, без шаблонов. C++11 и шаблоны, разумеется, дадут больше вариантов.

class Object < public: virtual ~Object() <>>; class List < // interface protected: virtual Object& getAt(size_t i) = 0; public: virtual size_t size() const = 0; inline Object& at(size_t i) < return getAt(i); >inline const Object& at(size_t i) const < return const_cast(this)->getAt(i); > virtual ~List() <> >;

Вариант 2. С ним пользователю не так просто, но если к нему ещё добавить лямбда-функции C++11 — вообще бомба будет!

class ListCallback < // interface public: virtual void act(size_t index, Object& object) = 0; virtual ~ListCallback() <>>; class ListConstCallback < // interface public: virtual void act(size_t index, const Object& object) = 0; virtual ~ListConstCallback() <>>; class List2 < // interface public: virtual size_t size() const = 0; virtual void enumerate(ListCallback& body) = 0; virtual void enumerate(ListConstCallback& body) const = 0; >;

Например, для динамического массива, у которого быстрый доступ по номеру, будет такое тело

void DynArray::enumerate(ListConstCallback& body) const

Разумеется, если вы нагрузкой сделаете не абстрактный Object, а что-то окончательное, dynamic_cast не нужен будет.

Вариант 3 принят в Java. Один недостаток — мого кода писать для const-корректности, так что с вашего позволения опущу.

class VirtualListIterator < // interface public: virtual bool next() = 0; virtual Object& value() = 0; virtual ~VirtualListIterator() <>>; class ListIterator < // Назван так для красоты, по сути это умный указатель private: VirtualListIterator* ptr; // Запретим копирование и op=, но при желании можно реализовать и их ListIterator(ListIterator&) <>void operator=(const ListIterator&) <> public: ListIterator() : ptr(NULL) <> ListIterator(VirtualListIterator* value) : ptr(value) <> ~ListIterator() < delete ptr; >bool next() < return ptr->next(); > Object* operator->() const < return &ptr->value(); > Object& operator*() const < return ptr->value(); > >; class List3 < // interface public: virtual size_t size() const = 0; virtual VirtualListIterator* enumerate() = 0; >; class Number : public Object < public: int value; >; // И пользоваться этим так. void doSmth(List3& aList) < ListIterator it(aList.enumerate()); while (it.next()) < std::cout (*it).value >

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

Да, и из вас будет хороший инженер, если вы сразу же задумываетесь о производительности.

Абстрактные классы и методы

Абстрактные методы – это методы у которых отсутствует реализация.

аbstract тип имяМетода(списокПараметоров); 
public abstract double calculateArea();

Абстрактные методы должны быть обязательно переопределены в подклассе.

Любой класс, содержащий один или больше абстрактных методов, должен быть также объявлен как абстрактный. Для этого достаточно указать ключевое слово abstract перед ключевым словом class в начале объявления класса:

public abstract class Figure2

У абстрактного класса в Java не может быть никаких объектов. Но абстрактные классы можно применять для создания ссылок на объекты. Например:

public abstract class Figure2 < . public abstract double calculateArea(); public static void main(String[] args) < // У абстрактного класса не может быть никаких объектов. // Figure2 figure1 = new Figure2(); // Но абстрактные классы можно применять для создания ссылок на объекты. Figure2 figure2 = new Rectangle(); >>

Также нельзя объявлять абстрактные конструкторы или абстрактные статические методы.

Рассмотрим пример использования абстрактных классов и методов на примере класса Figure и его наследников Triangle и Rectangle , которые мы рассматривали в Переопределение методов. Класс Figure описывает абстрактную фигуру, для которой вычисление площади не имеет смысла, поэтому метод calculateArea() более правильно сделать абстрактным.

Абстрактные классы в Java фото

public abstract class Figure2 < double dimension1; double dimension2; public Figure2(double dimension1, double dimension2) < this.dimension1 = dimension1; this.dimension2 = dimension2; >public abstract double calculateArea(); >

Любой подкласс, производный от абстрактного класса, должен реализовать все абстрактные методы из своего супер класса или же сам быть объявлен абстрактным:

public class Rectangle2 extends Figure2 < public Rectangle2(double dimension1, double dimension2) < super(dimension1, dimension2); >public double calculateArea() < System.out.println("B области четырехугольника."); return dimension1 * dimension2; >>
public class Triangle2 extends Figure2 < public Triangle2(double dimension1, double dimension2) < super(dimension1, dimension2); >public double calculateArea() < System.out.println("B области треугольника."); return dimension1 * dimension2 / 2; >>

У абстрактного класса не может быть объектов, но можно создать массив абстрактного класса, который будет содержать ссылки на объекты классов наследников:

public class FindAreas < public static void main(String[] args) < Figure[] figures = new Figure[3]; figures[0] = new Figure(10, 10); figures[1] = new Rectangle(10, 10); figures[2] = new Triangle(10, 10); for (Figure figure : figures) < double area = figure.caclulateArea(); System.out.println(area); >> >

Результат выполнения программы:

B области четырехугольника. 200.0 B области четырехугольника. 100.0 B области треугольника. 50.0
  • Процедурное и объектно-ориентированное программирование
  • Принципы ООП
  • Классы и объекты
  • Конструктор
  • Ключевое слово this
  • Перегрузка
  • Стек и куча
  • Передача объектов в методы
  • Java varargs
  • Рекурсия
  • Сборщик мусора и метод finalize
  • Наследование
  • Ключевое слово super
  • Модификаторы доступа
  • Геттеры и сеттеры
  • Переопределение методов
  • Ключевое слово final
  • Задания

Как создать массив абстрактного класса с

Иногда возникает необходимость определить класс, который не предполагает создания конкретных объектов. Например, класс фигуры. В реальности есть конкретные фигуры: квадрат, прямоугольник, треугольник, круг и так далее. Однако абстрактной фигуры самой по себе не существует. В то же время может потребоваться определить для всех фигур какой-то общий класс, который будет содержать общую для всех функциональность. И для описания подобных сущностей используются абстрактные классы.

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

Что такое чистые виртуальные функции (pure virtual functions)? Это функции, которые не имеют определения. Цель подобных функций - просто определить функционал без реализации, а реализацию определят производные классы. Чтобы определить виртуальную функцию как чистую, ее объявление завершается значением "=0". Например, определим абстрактный класс, который представляет геометрическую фигуру:

class Shape < public: virtual double getSquare() const = 0; // площадь фигуры virtual double getPerimeter() const = 0; // периметр фигуры >;

Класс Shape является абстрактным, потому что он содержит как минимум одну чистую виртуальную функцию. А в данном случае даже две таких функции - для вычисления площади и периметра фигуры. И ни одна из функций не имеет никакой реализации. В данном случае обе функции являются константными, но это необязательно. Гловно, чтобы любой производный класс от Shape должен будет предоставить для этих функций свою реализацию.

При этом мы не можем создать объект абстрактного класса:

Shape shape<>;

Для применения абстрактного класса определим следующую программу:

#include class Shape < public: virtual double getSquare() const = 0; // площадь фигуры virtual double getPerimeter() const = 0; // периметр фигуры >; class Rectangle : public Shape // класс прямоугольника < public: Rectangle(double w, double h) : width(w), height(h) < >double getSquare() const override < return width * height; >double getPerimeter() const override < return width * 2 + height * 2; >private: double width; // ширина double height; // высота >; class Circle : public Shape // круг < public: Circle(double r) : radius(r) < >double getSquare() const override < return radius * radius * 3.14; >double getPerimeter() const override < return 2 * 3.14 * radius; >private: double radius; // радиус круга >; int main() < Rectangle rect; Circle circle; std::cout

Здесь определены два класса-наследника от абстрактного класса Shape - Rectangle (прямоугольник) и Circle (круг). При создании классов-наследников все они должны либо определить для чистых виртуальных функций конкретную реализацию, либо повторить объявление чистой виртуальной функции. Во втором случае производные классы также будут абстрактными.

В данном же случае и Circle, и Rectangle являются конкретными классами и реализуют все виртуальные функции.

Консольный вывод программы:

Rectangle square: 1500 Rectangle perimeter: 160 Circle square: 2826 Circle perimeter: 188.4

Стоит отметить, что абстрактный класс может определять и обычные функции и переменные, может иметь несколько конструкторов, но при этом нельзя создавать объекты этого абстрактного класса. Например:

#include class Shape < public: Shape(int x, int y): x, y <> virtual double getSquare() const = 0; // площадь фигуры virtual double getPerimeter() const = 0; // периметр фигуры void printCoords() const < std::cout private: int x; int y; >; class Rectangle : public Shape // класс прямоугольника < public: Rectangle(int x, int y, double w, double h) : Shape, width(w), height(h) < >double getSquare() const override < return width * height; >double getPerimeter() const override < return width * 2 + height * 2; >private: double width; // ширина double height; // высота >; class Circle : public Shape // круг < public: Circle(int x, int y, double r) : Shape, radius(r) < >double getSquare() const override < return radius * radius * 3.14; >double getPerimeter() const override < return 2 * 3.14 * radius; >private: double radius; // радиус круга >; int main() < Rectangle rect; rect.printCoords(); // X: 0 Y: 0 Circle circle; circle.printCoords(); // X: 10 Y: 20 >

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *