C++ 派生类和多态调度
C++ Derived classes and polymorphic dispatching
请考虑以下代码段:
#include <iostream>
#include <string>
enum Type { T1, T2 };
class Base {
public:
std::string baseName;
Type type;
Base(const std::string& bn, Type t):
baseName(bn), type(t) {}
};
class Derived1 : public Base
{
public:
std::string dName;
int x = 10;
Derived1(const std::string& bn, const std::string& dn):
Base(bn, Type::T1), dName("Dervied1"+dn) {}
int getX(void) const { return x; }
};
class Derived2 : public Base
{
public:
std::string dName;
int y = 20;
Derived2(const std::string& bn, const std::string& dn):
Base(bn, Type::T2), dName("Derived2"+dn){}
int getY(void) const { return y; }
};
void func(Base& b)
{
if (b.type == Type::T1)
{
Derived1& d1 = static_cast<Derived1&>(b);
std::cout << d1.baseName << " " << d1.dName << " " << d1.getX();
std::cout << std::endl;
}
else
{
Derived2& d2 = static_cast<Derived2&>(b);
std::cout << d2.baseName << " " << d2.dName << " " << d2.getY();
}
};
int main(void)
{
Derived1 d1("Base", "foo");
func(d1);
Derived2 d2("Base", "foo");
func(d2);
}
要求是有一个可以接受基类值的函数,然后根据派生实例的"类型"执行其他操作。我的问题是 - 这是正确的做事方式还是我错过了一些重要的设计模式。我记得读到过使用static_cast或dynamic_cast意味着设计存在固有的问题。我知道理想情况下,基类可以具有派生类实现的虚函数,并且在运行时它们被多态调度。但是,在这种情况下,每个派生类中有两个特定于这些类的函数,即 getX 和 getY。我怎样才能改变设计以使其更好,也许不使用演员表?
谢谢!
要求是有一个可以接受基类值的函数,然后根据派生实例的"类型"执行其他操作。
这正是多态性的全部意义所在。 但是你没有按照它应该被使用的方式使用它。
我的问题是 - 这是正确的做事方式吗
不。
我是否缺少一些重要的设计模式。
通过完全摆脱Type
并在Base
中引入虚拟方法可以更好地处理这个问题。
我知道理想情况下,基类可以具有派生类实现的虚函数,并且在运行时它们被多态调度。
完全。
但是,在这种情况下,每个派生类中有两个特定于这些类的函数,即 getX 和 getY。
所以? 正确使用多态性并不能阻止这种情况。
我怎样才能改变设计以使其更好,也许不使用演员表?
正确使用多态性。例如:
#include <iostream>
#include <string>
class Base
{
public:
std::string baseName;
Base(const std::string& bn):
baseName(bn) {}
virtual void doIt() = 0;
};
class Derived1 : public Base
{
public:
std::string dName;
int x = 10;
Derived1(const std::string& bn, const std::string& dn):
Base(bn), dName("Dervied1"+dn) {}
int getX(void) const { return x; }
void doIt() override
{
std::cout << baseName << " " << dName << " " << getX();
std::cout << std::endl;
}
};
class Derived2 : public Base
{
public:
std::string dName;
int y = 20;
Derived2(const std::string& bn, const std::string& dn):
Base(bn), dName("Derived2"+dn) {}
int getY(void) const { return y; }
void doIt() override
{
std::cout << baseName << " " << dName << " " << getY();
}
};
void func(Base& b)
{
b.doIt();
}
int main(void)
{
Derived1 d1("Base", "foo");
func(d1);
Derived2 d2("Base", "foo");
func(d2);
}
然后更进一步,移动公共代码,以便派生类可以共享它:
#include <iostream>
#include <string>
class Base
{
public:
std::string baseName;
Base(const std::string& bn):
baseName(bn) {}
virtual void doIt()
{
std::cout << baseName;
}
};
class Derived : public Base
{
public:
std::string dName;
Derived(const std::string& bn, const std::string& dn):
Base(bn), dName(dn) {}
void doIt() override
{
Base::doIt();
std::cout << " " << dName;
}
};
class Derived1 : public Derived
{
public:
int x = 10;
Derived1(const std::string& bn, const std::string& dn):
Derived(bn, "Dervied1"+dn) {}
int getX(void) const { return x; }
void doIt() override
{
Derived::doIt();
std::cout << " " << getX();
std::cout << std::endl;
}
};
class Derived2 : public Derived
{
public:
int y = 20;
Derived2(const std::string& bn, const std::string& dn):
Derived(bn, "Derived2"+dn) {}
int getY(void) const { return y; }
void doIt() override
{
Derived::doIt();
std::cout << " " << getY();
}
};
void func(Base& b)
{
b.doIt();
}
int main(void)
{
Derived1 d1("Base", "foo");
func(d1);
Derived2 d2("Base", "foo");
func(d2);
}
如果可以选择使用virtual
成员函数,如其他答案所述,这是最佳使用方法。但是,在某些情况下,您没有这种奢侈。在这种情况下,您可以基于派生类型的类型构建调度机制。
#include <iostream>
#include <string>
#include <map>
class Base {
public:
std::string baseName;
Base(const std::string& bn): baseName(bn) {}
virtual ~Base() {}
// Don't store type ID per instance.
// Make it a virtual function so derived classes
// can return the same value for each instance.
virtual int getTypeID() = 0;
// Helper function for derived classes to use so each
// derived class can have a unique type id associated
// with it. This eliminates the need for having an enum.
static int getNextTypeID();
{
static int typeID = 0;
return ++typeID;
}
};
class Derived1 : public Base
{
public:
std::string dName;
int x = 10;
Derived1(const std::string& bn,
const std::string& dn):
Base(bn), dName("Dervied1"+dn) {}
// get type ID for this class.
// Every instance of the class will return
// same value.
virtual int getTypeID()
{
return getTypeIDStatic();
}
// This is a crucial piece of function
// that allows type based dispatch mechanism to work.
static int getTypeIDStatic()
{
static int typeID = Base::getNextTypeID();
return typeID;
}
int getX(void) const { return x; }
};
class Derived2 : public Base
{
public:
std::string dName;
int y = 20;
Derived2(const std::string& bn,
const std::string& dn):
Base(bn), dName("Derived2"+dn){}
int getY(void) const { return y; }
virtual int getTypeID()
{
return getTypeIDStatic();
}
static int getTypeIDStatic()
{
static int typeID = Base::getNextTypeID();
return typeID;
}
};
// Define a function type.
using Function = void (*)(Base& b);
// Keep a registry of functions that can be called for
// different types derived from Base.
std::map<int, Function>& getRegisteredFunctionsMap()
{
static std::map<int, Function> functionsMap;
return functionsMap;
}
// Provide a mechanism to register functions for types
// derived from Base.
template <typename T>
void registerFunction(Function f)
{
getRegisteredFunctionsMap()[T::getTypeIDStatic()] = f;
}
void func(Base& b)
{
// Check whether there is a function base on the type of b.
std::map<int, Function>& functionsMap = getRegisteredFunctionsMap();
std::map<int, Function>::iterator iter = functionsMap.find(b.getTypeID());
if ( iter != functionsMap.end() )
{
// If yes, call it.
iter->second(b);
}
else
{
// No function to deal with the type.
// Deal with the situation.
}
};
// A function that can be called when the real type is Derived1.
void derived1Fun(Base& b)
{
// Assume that b is derived.
Derived1& d1 = dynamic_cast<Derived1&>(b);
// Now use d1.
std::cout << d1.baseName << " " << d1.dName << " " << d1.getX();
std::cout << std::endl;
}
// A function that can be called when the real type is Derived2.
void derived2Fun(Base& b)
{
// Assume that b is Derived2.
Derived2& d2 = dynamic_cast<Derived2&>(b);
// Now use d2.
std::cout << d2.baseName << " " << d2.dName << " " << d2.getY();
std::cout << std::endl;
}
int main(void)
{
// Register functions for Derived1 and Derived2.
registerFunction<Derived1>(derived1Fun);
registerFunction<Derived2>(derived2Fun);
// Make the function calls.
Derived1 d1("Base", "foo");
func(d1);
Derived2 d2("Base", "foo");
func(d2);
}
运行上述程序的输出:
Base Dervied1foo 10
Base Derived2foo 20
相关文章:
- 多态性和功能结合
- 具有默认模板参数的多态类的模板推导失败
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 多态二进制函数
- 访问存储在向量C++中的结构的多态成员
- 使用取消引用的指针的多态性会产生意外的结果.为什么?
- 将 std::allocate_shared 与多态资源分配器一起使用
- 通过switch和static_cast访问多态对象的运行时类型
- C++boost序列化多态性问题
- 多态杆件变量 - 类设计
- 如何查找哪个类对象位于数组的特定索引上(多态性)
- 如何在多线程中正确使用unique_ptr进行多态性?
- Doees the 'this' 指针参与虚函数的多态行为
- C++ 在堆栈中包含多态属性的类对象存储
- 基类和派生类的多态赋值运算符
- 转身多态对象
- 混合双重调度和静态多态性
- C++ 派生类和多态调度
- 具有双重调度的C++多态循环函数
- 使用boost::shared_ptr的多态调度