#c
#c
Вопрос:
Я получил абстрактный базовый класс, который выглядит следующим образом:
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.
Комментарии:
1. да, это возможно, просто сделайте это. Не ясно, в чем проблема / вопрос. Если вы столкнулись с проблемами, вы должны сообщить
2. чистый абстрактный деструктор?
3. Упс. Я забыл добавить, что я получаю ошибку LinkerError… Код ошибки 2019.
4. после проверки вашего кода я понял, в чем ваша «проблема». Похоже, у вас сложилось впечатление, что вы могли бы переопределить члены класса. Пожалуйста, прочитайте о проблеме xy и объясните, чего вы на самом деле хотите достичь
5. В любом случае я вижу, что люди переходят к этой проблеме dtor и не заметили, что в вашей строке кода совпадает имя класса. Итак, похоже, что это проблема XY (я проголосовал за пользователя 463035818), и я жду возведения проблемы X в степень.
Ответ №1:
Используйте только одно имя в базовом классе и конструктор с параметром:
class AbstractClass{
public:
AbstractClass(const std::stringamp; 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() {}
Это создает класс, который не может быть создан, но производные классы которого все еще могут быть уничтожены.
Ответ №2:
Это не то, как вы «переопределяете» 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::stringamp; 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"; }
};
Ответ №3:
Вы получаете ошибку ссылки, потому что деструктор для AbstractClass
должен быть определен, даже если он пуст.
AbstractClass::~AbstractClass()
{
// Compulsory virtual destructor definition,
// even if it's empty
}
Что касается переопределения getName
: вам не обязательно. Если вы не предоставляете реализацию в производном классе, используется унаследованный.
Комментарии:
1. Хорошо. Но я все равно хочу, чтобы Класс был абстрактным. Как мне этого добиться?
2. Это уже абстрактно. если вы попытаетесь объявить объект
AbstractClass
типа, вы увидите, что ошибка компилятора будет довольно ясна по этому поводу.3. @HenningWilmer Абстрактность просто означает, что у вас есть хотя бы одна чисто виртуальная функция. Это не то, что требует код, это то, что происходит , потому что что-то в классе должно быть абстрактным. Исходя из текущих потребностей вашего класса (метод getName и ничего больше), ваш класс еще не является абстрактным, но, скорее всего, станет таковым (при необходимости) по мере добавления реальной функциональности.
4. @DavideSpataro Хорошо, спасибо! У меня это почти сработало. Я больше не получаю ошибку, но и не получаю выходных данных. Ах, я вижу, потому что m_Name абстрактного класса пуст..
Ответ №4:
Предполагается, что проблема в том, как получить имя класса? Но это четко не указано в вопросе (проблема 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 может очистить его: