абстрактный класс — Абстракция данных и двоичные методы в переполнении стека

Я новичок в C ++ и пытаюсь понять абстракцию данных в сочетании с бинарными методами, такими как равенство. Я хотел бы определить интерфейс

class A {
public:
static A* Make(int);
virtual ~A() {};
virtual bool Eq(A*) = 0;
};

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

class B : public A {
public:
B(int x) : x_(x) {}

bool Eq(A* a) {
return x_ == dynamic_cast<B*>(a)->x_;
}

private:
int x_;
};

A* A::Make(int x) {
return new B(x);
}

Затем я могу использовать абстракцию:

A* a = A::Make(1);
A* b = A::Make(2);

if (a->Eq(b)) {
cout << "Yes!" << endl;
} else {
cout << "No!" << endl;
}

Тем не менее, динамический бросок отстой по ряду причин. Самая большая проблема, как я вижу, состоит в том, что можно создать подкласс A с C, а затем передать объект типа C в a-> Eq, что приведет к неопределенному поведению. Я не могу понять, как определить двоичный метод так, чтобы код имел доступ к закрытым членам обоих объектов. Моя интуиция заключается в том, что это можно сделать с помощью шаблона посетителя, но я не смог найти решение.

Для тех, кто знает ML, я хочу сделать следующее:

module A : sig
type t
val make: int -> t
val eq: t -> t -> bool
end = struct
type t = int
let make x = x
let eq x y = (x = y)
end

2

Решение

Развернув мой комментарий, вы можете написать свой код, используя идиому PIMPL (указатель на реализацию) следующим образом:

В вашем заголовочном файле (a.hpp)

class A
{
public:
A(int);
~A();
bool Eq(const A& a) const;

private:
class AImpl;
AImpl* implementation_;
};

В файле реализации (a.cpp)

struct A::AImpl {
AImpl(int x) : x_(x) {}
int x_;
};

A::A(int x)
: implementation_(new AImpl(x))
{}

A::~A()
{
delete implementation_;
}

bool
A::Eq(const A& rhs) const
{
return implementation_->x_ == rhs.implementation_->x_;
}

В вашем основном файле

#include "a.hpp"
int main()
{
A one(1);
A two(2);

return one.Eq(two);
}

Поскольку весь реальный код находится в файле реализации, вы не сможете увидеть его из других классов (которые будут включать только заголовочный файл a.hpp). Для ясности я использовал необработанный указатель, но в реальной ситуации вы бы хотели использовать соответствующий умный указатель.

1

Другие решения

Вы не хотите делать функции равенства виртуальными.

Вот пример:

class Animal
{
public:
virtual bool is_equal(const Animal * p_a) = 0;
};

class Human : public Animal
{
public:
bool is_equal(const Animal * p_a);
};

class Toad : public Animal
{
public:
bool is_equal(const Animal * p_a);
};

Если я создам две переменные указателей на животных:

   Animal * p_animal_human = new Human;
Animal * p_animal_toad  = new Toad;

Виртуальный is_equal Метод позволяет мне сравнить два класса животных:

   if (p_animal_human->is_equal(p_animal_toad)

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

В выше if заявление, только Animal данные, общие для обоих классов, сравниваются.

Поиск StackOverflow по термину «нарезка [C ++]».

2

Вы можете использовать что-то вроде:

class A {
public:
virtual ~A() {};
virtual bool Eq(const A& a) const = 0;
};

class B : public A {
public:
explicit B(int x) : x_(x) {}

virtual bool Eq(const A& a) const {
const B* b = dynamic_cast<const B*>(&a);
return b != NULL && Eq(*b);
}
bool Eq(const B& rhs) const { return x_ == rhs.x_; }

private:
int x_;
};

class C : public A {
public:
explicit C(int x) : x_(x) {}

bool Eq(const A& a) const {
const C* c = dynamic_cast<const C*>(&a);
return c != NULL && Eq(*c);
}
bool Eq(const C& rhs) const { return x_ == rhs.x_; }

private:
int x_;
};

int main() {
B b1(42);
B b2(42);
C c(42);
A& ab1 = b1;
A& ab2 = b2;
A& ac = c;

std::cout << "ab1.Eq(ab2) = " << ab1.Eq(ab2) << std::endl; // true
std::cout << "ab1.Eq(ac) = " << ab1.Eq(ac) << std::endl;   // false

return 0;
}
0

  • «Я не могу понять, как определить двоичный метод так, чтобы код имел доступ к закрытым членам обоих объектов»
  • В C ++ вы можете получить доступ к закрытым функциям-членам и защищенным функциям-членам из других классов или типов объектов с помощью ключевого слова friend.
  • Вам необходимо включить класс, который содержит закрытые функции-члены или функции-члены-защитники, в файл заголовка, такой как;
  • класс А {
  • друг класс B;
  • }
  • И вы также можете сделать это наоборот. Таким образом, класс B может быть другом класса A, а класс A может быть другом класса B.
0
По вопросам рекламы [email protected]