У меня есть абстрактный базовый класс, который выглядит так:
class AbstractClass {
public:
virtual ~AbstractClass() = 0 {}
std::string GetName() const { return m_Name; }
private:
std::string m_Name;
};
Теперь у меня есть много производных классов, и я хочу реализовать их следующим образом.
class DerivedClass1 : public AbstractClass{
public:
DerivedClass1() = default;
~DerivedClass1() = default;
private:
std::string m_Name = "DerivedClass1";
};
int main() {
DerivedClass1 class1;
std::cout << class1.GetName();
return 0;
}
Я не хочу переопределять GetName() каждый раз, когда я получаю класс, возможно ли это? Редактировать: У меня ошибка компоновщика. Ошибка LNK2019.
чистый абстрактный деструктор?
Упс. Я забыл добавить, что получаю LinkerError... ErrorCode 2019.
гул после проверки вашего кода я понял, в чем ваша «проблема». Кажется, у вас сложилось впечатление, что вы можете переопределить членов класса. Прочтите о проблема ху и объясните, чего вы на самом деле хотите достичь
@MarekR Да, я где-то читал, что это возможно, и я явно не хочу делать GetName виртуальным.
@HenningWilmer, это был риторический вопрос, он объясняет проблему со ссылками, видеть это
Во всяком случае, я вижу, что люди прыгают по этой проблеме с dtor и не замечают, что в вашей строке кода совпадает имя класса. Итак, похоже, что это проблема XY (я проголосовал за пользователя 463035818), и я жду возведения в степень проблемы X.
Стоит упомянуть (хотя, вероятно, это не ваша основная проблема), что DerivedClass1 class1; std::cout << class1.GetName();
не использует динамическую диспетчеризацию (т.е. не заботится о том, являются ли ваши функции virtual
/полиморфными/переопределенными где-либо). Вам нужен указатель или ссылка для правильного использования виртуальных функций (прочитайте о динамическом и статическом типах).
Вы получаете ошибку ссылки, потому что деструктор для AbstractClass
должен быть определен, даже если он пуст.
AbstractClass::~AbstractClass()
{
// Compulsory virtual destructor definition,
// even if it's empty
}
Что касается переопределения getName
: вам не нужно. Если вы не предоставляете реализацию в производном классе, используется унаследованная.
Хорошо. Но я все еще хочу, чтобы класс был абстрактным. Как мне этого добиться?
Это уже абстрактно. если вы попытаетесь объявить объект типа AbstractClass
, вы увидите, что ошибка компилятора будет довольно ясной.
@HenningWilmer Абстрактность просто означает, что у вас есть хотя бы одна чистая виртуальная функция. Это не то, что кодирует требует, это то, что бывает, потому что что-то в классе должно быть абстрактным. Основываясь на текущих потребностях вашего класса (метод GetName и ничего больше), ваш класс еще не является абстрактным, но, вероятно, станет таковым (при необходимости) по мере добавления реальной функциональности.
@DavideSpataro Хорошо, спасибо! Это почти сработало для меня. Я больше не получаю ошибку, но и не получаю вывод. Ах, я вижу, потому что m_Name класса AbstactClass пусто..
Используйте только одно имя в базовом классе и конструктор с параметром:
class AbstractClass{
public:
AbstractClass(const std::string& name) : m_Name(name){}
std::string GetName() const { return m_Name; }
private:
std::string m_Name;
};
DerivedClass1 : public AbstractClass{
public:
DerivedClass() : AbstractClass("DerivedClass1") {}
};
int main(){
DerivedClass1 class1;
std::cout << class1.GetName();
return 0;
}
Кажется, нет причин делать базовый класс абстрактным, но если вам делать это нужно, даже чистый виртуальный деструктор должен иметь определение, иначе вы получите ошибку компоновщика, потому что это необходимо при уничтожении производных объектов.
Кроме того, если бы деструктора не существовало, когда бы m_Name
был уничтожен?
class Abstract
{
public:
virtual ~Abstract() = 0;
};
Abstract::~Abstract() {}
Это создает класс, который не может быть создан, но чьи производные классы все еще могут быть уничтожены.
Это не то, как вы «переопределяете» GetName()
. Вы можете сделать GetName()
виртуальным и переопределить его в своих производных классах:
class AbstractClass {
public:
virtual ~AbstractClass() = default;
virtual std::string GetName() const { return "AbstractClass"; }
private:
std::string m_Name;
};
а также:
class DerivedClass1 : public AbstractClass {
public:
DerivedClass() = default;
std::string GetName() const override { return "DerivedClass1"; }
};
Или вы можете установить m_Name
в своих производных классах, передав его конструктору базового класса:
class AbstractClass {
public:
AbstractClass(const std::string& name) : m_Name(name) {}
virtual ~AbstractClass() = default;
std::string GetName() const { return m_Name; }
protected: // protected not private
std::string m_Name;
};
а также:
class DerivedClass1 : public AbstractClass {
public:
DerivedClass() : AbstractClass("DerivedClass1") {}
};
Или вы можете установить его в конструкторе производного класса:
class AbstractClass {
public:
virtual ~AbstractClass() = default;
std::string GetName() const { return m_Name; }
protected: // protected not private
std::string m_Name;
};
а также:
class DerivedClass1 : public AbstractClass {
public:
DerivedClass() : AbstractClass() { m_Name = "DerivedClass1"; }
};
Код предполагает, что проблема заключается в том, как получить имя класса? Но это не ясно указано в вопросе (проблема XY)
Как обрабатывать имя класса?
Вы можете использовать RTTI:
class ClassName {
public:
virtual ~ClassName() {} // just to enable RTTI for all decendants
std::string getClassName() {
return typeid(*this).name();
}
};
https://wandbox.org/permlink/LvPdA37arMr0LFQW
Но, как вы можете видеть, он добавляет дополнительный префикс (это зависит от компилятора). boost может очистить его:
да это возможно, просто сделайте это. Непонятно, в чем проблема/вопрос. Если вы столкнулись с проблемами, вы должны сообщить