Для чего *вы* используете конструкторы C++ ABC?
-
19-08-2019 - |
Вопрос
Для чего люди здесь используют конструкторы абстрактного базового класса C++ в полевых условиях?Я говорю о чистых классах интерфейса, не имеющих членов данных и нечистых виртуальных членов.
Может ли кто-нибудь продемонстрировать какие-либо идиомы, которые полезным образом используют конструкторы ABC?Или просто природе использования ABC для реализации интерфейсов свойственно то, что они остаются пустыми, встроенными и защищенными?
Решение
Может ли кто-нибудь продемонстрировать какие-либо идиомы, которые полезным образом используют конструкторы ABC?
Вот пример, хотя это надуманный и необычный пример.
Вы можете использовать его для хранения списка всех экземпляров:
class IFoo
{
private:
//static members to keep a list of all constructed instances
typedef std::set<IFoo*> Set;
static Set s_set;
protected:
//new instance being created
IFoo()
{
s_set.insert(this);
}
public:
//instance being destroyed
virtual ~IFoo()
{
s_set.remove(this);
}
... plus some other static method and/or property
which accesses the set of all instances ...
};
Или просто природе использования ABC для реализации интерфейсов свойственно то, что они остаются пустыми, встроенными и защищенными?
Чаще всего они просто не объявляются вообще!Нет причин их объявлять:
- Пустой и встроенный => зачем это объявлять?
- Защищено => в ABC, вероятно, уже есть некоторые чистый виртуальный методы и, следовательно, уже не могут быть созданы, кроме как в качестве подкласса.
Другие советы
Предположим, что существует некоторое поведение, общее для всех производных классов. Например, зарегистрировать себя в каком-либо внешнем реестре или проверить действительность чего-либо.
Весь этот общий код можно поместить в конструктор базового класса, и он будет вызываться неявно из конструкторов каждого из производных классов.
Как можно использовать конструктор абстрактного базового класса для чего-либо?
Предположим, у вас есть абстрактный базовый класс B и производный класс D. Когда создается объект типа D, сначала вызывается конструктор B, но в этот момент объект " is " тип B (см. здесь ) - в частности, вызов любых виртуальных функций из тела конструктора B вызовет собственные реализации B этих функций. Но если B является чисто абстрактным классом, ни одна из этих виртуальных функций не определена, поэтому программа немедленно завершит работу.
Я предполагаю, что вы хотели, чтобы конструктор B вызывал реализацию виртуальной функции самого производного класса (например, D), верно? В целом это было бы плохой идеей, поскольку объект D еще не полностью сконструирован, поэтому любой доступ к переменным-членам в D изнутри реализации виртуальной функции D мог бы получить доступ к неинициализированной памяти.
Помнить: «Получение ресурса — это инициализация».
Иногда мы используем абстрактные базовые классы в качестве своего рода механизма блокировки.Например, в многопоточной среде, где нескольким потокам необходимо совместно использовать один ресурс, поток может использовать конструктор как способ получения ресурса, а деструктор — для его освобождения.
void PlayWithPaintBallGun(Target &target)
{
PaintBallGun paintBallGun; // constructor waits until the gun is free,
// then picks it up.
paintBallGun.Aim(target); // Shoot something
paintBallGun.Fire(); //
// Clever! The destructor is automatically
// called when it goes out of scope. So we
// can't forget to put the gun down.
}
Хьюго
Я не могу придумать много полезных примеров. Класс без данных-членов не имеет состояния и поэтому не может ничего инициализировать. Вы можете сделать так, чтобы конструктор / деструктор занялся ведением журнала для вас. Например, чтобы зарегистрировать создание / уничтожение всех объектов Visitor:
class Visitor {
public:
Visitor() {
std::cout << "Visitor@" << this << " created"
<< std::endl;
}
virtual ~Visitor() {
std::cout << "Visitor@" << this << " destroyed"
<< std::endl;
}
virtual void visitA(A*) = 0;
virtual void visitB(B*) = 0;
// ...
};
обычно его инициализируют только члены с разумными ценностями.