C++ 在基类中保存类型是否被认为是糟糕的编程
我想知道编译时我的类的类型,我想知道我的想法是否被认为是糟糕的编程,或者它是否实际可行。如果有更好的方法来实现这一点,请纠正我C++ 在基类中保存类型是否被认为是糟糕的编程,c++,derived-class,C++,Derived Class,我想知道编译时我的类的类型,我想知道我的想法是否被认为是糟糕的编程,或者它是否实际可行。如果有更好的方法来实现这一点,请纠正我 class Base { int type = 0; } class Derivative : public Base{ Derivative(){ type = 1; SomeObject1 o; SomeAnotherObject o1; } } class Derivative2 : public Bas
class Base {
int type = 0;
}
class Derivative : public Base{
Derivative(){
type = 1;
SomeObject1 o;
SomeAnotherObject o1;
}
}
class Derivative2 : public Base{
Derivative2(){
type = 2;
RandomObject test;
AnotherObject v;
}
}
将myBaseClass
作为Base
获取的某些方法:
if(myBaseClass.type == 1){
Derivative d = static_cast<Derivative>(myBaseClass);
d.o;
d.o1;
}
if(myBaseClass.type == 2){
Derivative2 d = static_cast<Derivative2>(myBaseClass);
d.test;
d.v;
}
if(myBaseClass.type==1){
导数d=静态_转换(myBaseClass);
d、 o;
d、 o1;
}
if(myBaseClass.type==2){
派生2 d=静态转换(myBaseClass);
d、 试验;
d、 五,;
}
在我看来,为所有不同的对象编写虚拟方法是不寻常的这是几年前我在编写pdf编写器时使用的(丑陋的)方法。它似乎解决了与您完全相同的问题
pdfArray::pdfArray(const pdfArray &src)
{
vecObjPtrIter iter;
pdfObj *ptr;
mArray = new vecObjPtr;
for (iter=src.mArray->begin(); iter!=src.mArray->end(); iter++)
{
ptr = *iter;
if (typeid(*ptr) == typeid(pdfString))
addItem( (pdfString*)ptr );
if (typeid(*ptr) == typeid(pdfInt))
addItem( (pdfInt*)ptr );
if (typeid(*ptr) == typeid(pdfFloat))
addItem( (pdfFloat*)ptr );
if (typeid(*ptr) == typeid(pdfArray))
addItem( (pdfArray*)ptr );
}
}
在基类中保存类型是否被认为是糟糕的编程
当然,是的!
使用多态虚拟设计,您不需要将额外的信息存储到基类中。编译器已经为您执行了以下操作:
class Base {
protected:
virtual ~Base() {} // <<<<<<<<<<<<<
}; // Note the ;!
class Derivative : public Base{
};
class Derivative2 : public Base{
};
不过,如果你需要知道这一点,这是一个糟糕设计的严重指标
您应该以纯虚拟函数定义的形式引入一些接口:
class Base {
protected:
virtual ~Base() {}
public:
virtual void DoSomething() = 0;
};
class Derivative : public Base{
public:
void DoSomething() override {
// provide an implementation specific for Derivative
}
};
class Derivative2 : public Base{
public:
void DoSomething() override {
// provide an implementation specific for Derivative2
}
};
这允许您调用DoSomething()
,而不知道实现该函数的特定类型:
Base* pd1 = new Derivative();
Base* pd2 = new Derivative2();
pd1->DoSomething(); // calls Derivative specific implementation
pd2->DoSomething(); // calls Derivative2 specific implementation
要安全有效地使用
静态\u cast
请改用:
模板
阶级基础{
公众:
无效剂量测定法(){
静态施法(本)->DoSomething();
}
};
类别衍生工具:公共基础{
};
类派生2:公共基{
};
这种技术的使用至少是合理的。我见过的一个涉及类层次结构,其实例需要由用户配置(由Python驱动),然后在性能关键代码中使用(在C++中)。基类提供了一个返回枚举的getType()
方法;Python中的包装器代码调用它来发现向用户提供哪个接口。跨语言代码通常强制使用基于一致同意的整数标签的简单技术
更一般地说,有时好的设计原则,如MVC,会鼓励这种安排。即使不同的层是用同一种语言编写的,底层模型对象也不一定有像makeqtwidts()
这样的方法,因为它要求该层不仅要知道GUI库,还要知道用户界面的布局和控制流
一个实用点:为了避免派生类无法指定其类型的情况,基类应该要求其构造函数中的值:
struct Base {
enum Type { derived1, derived2 };
Base(Type t) : typ(t) { /* ... */ }
virtual ~Base()=0;
Type getType() const {return typ;}
// ...
private:
Type typ;
};
struct Derived1 : Base {
Derived1() : Base(derived1) { /* ... */ }
// ...
};
您还可以将所有可能的enum
放在基类中,因为每个派生类的值都必须有一个中心注册表,即使它只是在纸上。除了其他人提到的几个类之外,这是一个缺点:这种设计要求所有类都集中管理,不可能独立扩展
最后,尽管缺乏灵活性,客户机必须始终面对意外类型对象的丑陋可能性:
void foo(const Base &b) {
switch(b.getType()) {
case Base::derived1: /* ... */ break;
case Base::derived2: /* ... */ break;
default:
// what goes here?
}
}
您将如何使用
类型????(除其他问题外)您的示例在调用构造函数的运行时之前不会设置类型变量。编译时永远不会发生在这个系统中。已经存在用于运行时确定的typeid
关键字。你更适合询问你试图解决的问题,而不是具体的解决方法。一个典型的X/Y问题。是的,C++的思想是在数据结构的开始时远离“类型”字段。如果你发现这是你想要的方式,请回到C。@melpomenedynamic\u cast
在我看来是不好的做法。如果我在Base
@CarmacMondradynamic\u cast
中为所有对象声明函数,这看起来不是不好,不是因为它本身就是错误的,但是因为它支持你试图在这里编写的程序。换句话说,您试图做的是“不好的”(但在不了解更多细节和上下文的情况下很难说),但dynamic\u cast
是实现它的最佳方式(当然比手动编码的整型字段要好)。:红脸:是的,这只是替换addItem((pdfArray*)ptr的问题代码>带有附加项(动态\u castpr)
等,不是吗?如果我最好实现addItem
成员,那就更好了。如果我调用DoSomething
在Base
中形成另一个方法,它是否也会调用导数的实现,如果它是一个?@CarmacMondra是的。除非从基
构造函数体中调用,否则它将被调用。这就是该功能的全部要点。与之密切相关。我需要使用CRTP吗?@CarmacMondra不,你不需要使用它。完全取决于您的实际用例。使用它有什么好处?
struct Base {
enum Type { derived1, derived2 };
Base(Type t) : typ(t) { /* ... */ }
virtual ~Base()=0;
Type getType() const {return typ;}
// ...
private:
Type typ;
};
struct Derived1 : Base {
Derived1() : Base(derived1) { /* ... */ }
// ...
};
void foo(const Base &b) {
switch(b.getType()) {
case Base::derived1: /* ... */ break;
case Base::derived2: /* ... */ break;
default:
// what goes here?
}
}