Я новичок в C++, и я решил провести эксперименты с указателями на указатели. Я пытаюсь создать систему, в которой я могу использовать и абстрактный класс, чтобы определить интерфейс, который многие не связанные между собой классы могут наследовать (часто через множественное наследование), чтобы затем я мог перетащить их на некоторые общие вспомогательные функции.
Я закипил код до такой степени, насколько могу, поэтому извиняюсь, если листинг немного длинный...
//------------ HEADER
struct IClassData
{
virtual int Width() = 0;
virtual int Height() = 0;
};
class ClassA
{
public:
ClassA(int w, int h);
protected:
int _Width;
int _Height;
};
class Derived : public ClassA, public IClassData
{
public:
Derived();
Derived(int w, int h);
virtual int Width() override;
virtual int Height() override;
};
class Computron
{
public:
static int TotalWidth(IClassData** data, int size)
{
int curSum = 0;
for (int i = 0; i < size; i++)
{
curSum += data[i]->Width();
}
return curSum;
}
};
// -------------- CPP
ClassA::ClassA(int w, int h)
{
_Width= w;
_Height = h;
}
Derived::Derived() : ClassA(0,0) { /* NONE */ }
Derived::Derived(int w, int h) : ClassA(w,h) { /* NONE */ }
int Derived::Width() { return _Width; }
int Derived::Height() { return _Height; }
Поэтому в моей основной программе я создаю несколько экземпляров производного класса, и я хочу сделать кастинг таким образом, чтобы он мог работать против таких функций:
// --------------- MAIN
...
const int MAX = 5;
Derived** data = new Derived*[MAX];
for (int i = 0; i < MAX; i++)
{
data[i] = new Derived(i, i*2);
}
// Cast an compute
int size = Computron::TotalWidth((IClassData**)data, MAX);
...
Поэтому мои большие вопросы:
1. Это нормально, или я буду стрелять в ногу позже? Я понимаю, что мои занятия здесь очень простые, но если они будут очень сложными, это вызовет некоторые проблемы на линии?
2. Кроме того, есть ли вероятность, что подобный прилив может не работать во время выполнения по какой-то причине?
3. Мне лучше использовать шаблон для этих типов сценариев?
Это нормально, или я буду стрелять в ногу позже? Я понимаю, что мои занятия здесь очень простые, но если они будут очень сложными, это вызовет некоторые проблемы на линии?
Я ничего здесь не вижу, что вообще кричит о проблемах, но я настоятельно рекомендую использовать стандартную библиотеку, чтобы избежать проблем с управлением памятью позже. Например, вместо вашего массива я бы использовал:
std::vector<std::unique_ptr<Derived>> data;
Когда data
выходят за рамки, вектор будет уничтожен. Это, в свою очередь, приведет к уничтожению unique_ptr и автоматически удалит Derived
объекты.
Однако вам нужны только указатели, в которых вам нужно быть полиморфными и хранить разные типы объектов в одном массиве; то есть, скорее всего, вам это понадобится, если вы храните указатели на объекты IClassData
, где вы точно не знаете, какой производный тип будет отображаться в массиве во время выполнения. Как написано, вы можете просто использовать std::vector<Derived>
потому что знаете, что все объекты будут Derived
экземплярами.
Кроме того, есть ли вероятность того, что подобный прилив может не работать во время выполнения по какой-то причине?
Не используйте C-style cast в C++. Вы должны использовать static_cast
здесь, и он даже не работает во время компиляции, поскольку он нарушает систему типов.
Derived ** a = new Derived*[MAX];
Base ** b = static_cast<Base **>(a); // This line will not compile, because...
b[0] = new Base(); // Now a[0] is a Base rather than a Derived??
a[0]->some_derived_method(); // What should happen!?!?
Мне лучше просто использовать шаблон для этих типов сценариев?
Каждый сценарий отличается, поэтому ответ является окончательным "возможно".
unique_ptr
требует C ++ 11 или более поздней unique_ptr
. В более ранних версиях был только auto_ptr
, который небезопасно использовать в контейнерах STL (одна из причин, по которой был создан unique_ptr
для замены auto_ptr
).