У меня есть некоторая структура классов для проекта, который я строю для своей компании. В какой-то момент я увидел, что «обычно нежелательные» фрагменты производного объекта могут фактически сделать мой код эффективным. Пожалуйста, обратите внимание:
class Base {
private:
int myType; //some constant here
public:
Base(): myType(1)
{
if(someTest())
{
myTest = 0; // signifies that this object is not working well for me
} // if the test succeeds, now I know this is a TYPE1 object
}
virtual bool someTest() { /* does make some validation tests */ }
int GetMyType() { return myType; }
// some more virtual functions follow
}
class Derived: public Base {
public:
Derived() : Base()
{
if( someTest() /*calls the derived*/ )
{
myType =2; // signifies that this is a TYPE2 object
/* Do something more*/
}
}
virtual bool someTest() { /* does make OTHER validation tests */ }
}
Теперь, если someTest () в Derived завершается с ошибкой, это показывает, что мой объект на самом деле имеет базовый тип (myType = 1) или неисправный объект (myType = 0). Так как эти вещи находятся в конструкторах, и так как я не могу использовать обработку исключений (встроенная система); Я думал об этом:
{
// this is the object holder test block
// there is some latch - Base* - above = let's call it "ObjectHolder"extern Base* ObjectHolder;Derived * deriv = new Derived();
int dType = deriv->GetMyType() ;
if( dType == 1) // if test at base succeded but the one in derived failed
Base = & static_cast<Base> (*deriv); // specifically slice the object
else if(dType =2)
Base = deriv;
else
delete deriv; // this is a TYPE0 - nonworking- object so don't latch it
// use other implemented methods to do work ...
}
Теперь, почему я получил такой дизайн? Ну, пока я проектировал классы, так как методы внутренней работы классов «Base» (тип 1) и «Derived» (тип 2) различаются (и есть возможность типа 3,4,5 …. объекты, которые также различаются внутри), и так как я не хотел делать и проверять «if-then-else» в каждом отдельном методе; Я думал, что сделаю такой дизайн и сделаю разные методы виртуальными, чтобы их можно было правильно вызывать (благодаря полиморфизму), в то время как общие методы могут быть в некотором базовом классе.
Но сейчас, во-первых, я не уверен, что этот странный синтаксис ( & static_cast < База> * производная) является правильной (проверено и, похоже, работает, но я просто хочу убедиться, что это не из-за удачи); и во-вторых, я не уверен, что это «предложенный метод» для достижения этой цели — я подозреваю, что этот синтаксис может вызвать утечки памяти или что-то …
Немного изменил код (также исправил синтаксис), чтобы прояснить проблему.
Теперь, так как &(static_cast (* производная)) является ошибочным подходом, я подумал, смогу ли я создать «конструктор копирования», который обходит проверку в базовом классе (что на самом деле является причиной того, что я пробую эти вещи — я не хочу этот тест быть запущенным в некоторых случаях). Как ниже:
class Base {
// other stuff is like above
// cc:
Base(const Base &other) : myType(other.myType)
{
// no test is performed here!
}
// like other one above
}
С этим написано, я думаю, что я могу сделать это сейчас в тестовом блоке:
{
// the code above
if( dType == 1) // if test at base succeded but the one in derived failed
Base = new Base(*deriv); // specifically slice the object with cc
// code goes on ...
}
Как насчет этого?
Конечно, если вы хотите создать два типа объектов, правильный способ — иметь фабричную функцию, которая содержит «if (sometest)», а затем создает правильный тип объекта:
Base* factory(...)
{
if(sometest())
{
return new Derived;
}
else
{
return new Base;
}
}
Таким образом, вы не будете без необходимости создавать производный тип объекта, когда вам действительно нужен объект базового типа. Имейте также в виду, что если вы нарезаете объект, ваш vtable
связанные с производными объектами, которые были нарезаны, по-прежнему будут производными vtable
,
Изменить: Вот некоторый код, чтобы показать проблему с нарезкой:
#include <iostream>
using namespace std;
class Base
{
public:
virtual void func() { cout << "Base" << endl; }
};class Derived : public Base
{
public:
virtual void func() { cout << "Derived" << endl; }
};int main()
{
Base *list[10];
for(int i = 0; i < 10; i++)
{
if (i % 2)
{
list[i] = new Base;
}
else
{
list[i] = new Derived;
}
}
list[2] = &*(static_cast<Base*>(list[2]));
for(int i = 0; i < 10; i++)
{
list[i]->func();
}
return 0;
}
Выход:
Derived
Base
Derived <- should have changed to "Base" if questio was "right".
Base
Derived
Base
Derived
Base
Derived
Base
Используя синтаксис для «нарезки», как указано в исходном вопросе:
&(static_cast<Base>(*list[2]));
(согласно адаптированному к коду, опубликованному здесь), дает ошибку в обоих clang++
а также g++
компилятор. Суть сообщения та же. Это вариант Clang:
error: taking the address of a temporary object of type 'Base'
Других решений пока нет …