c++ - 缩小派生类中指针成员的子类
问题描述
我有一个分为两个高级对象的类层次结构,以及低级和可交换的连接接口。连接对象形成一个类层次结构,其中每个对象都向连接添加更多功能。类似地,高级类层次结构需要逐渐更好的连接。
连接对象如下所示:
class BaseConnection {
virtual void a() = 0;
};
class BetterConnection : public BaseConnection {
virtual void b() = 0;
}
class BestConnection : public BetterConnection {
virtual void c() = 0;
}
这是我编写高级对象的尝试
struct Base {
protected:
// This type is correct for `Base`, but `Better` and `Best` need access to a more derived type.
unique_ptr<BaseConnection> const m_conn;
public:
Base(unique_ptr<BaseConnection> conn) : m_conn(std::move(conn));
void do_a_twice() {
auto& conn = *m_conn;
conn.a(); conn.a();
}
};
struct Better : public Base {
Better(unique_ptr<BetterConnection> conn) : Base(std::move(conn));
void do_b_twice() {
auto& conn = dynamic_cast<BetterConnection&>(*m_conn);
conn.b(); conn.b();
}
};
struct Best : public Better {
unique_ptr<BetterConnection> conn;
Better(unique_ptr<BetterConnection> conn) : Better(std::move(conn));
void do_c_twice() {
auto& conn = dynamic_cast<BestConnection&>(*m_conn);
conn.b(); conn.b();
}
};
所以,我的问题:
- 有没有办法做到这一点
dynamic_cast
? - 我是否认为这会导致使用运行时类型信息的运行时开销?
- 在这里使用安全
reinterpret_cast
吗?
解决方案
在我看来,围绕您的Connection
类型的抽象使事情变得更加困难(而抽象应该简化事情)。
为什么Connection
类型有不同的成员?如果派生Connection
类替代了BaseConnection
,您可以依靠虚函数调度在运行时做正确的事情。例如
struct BaseConnection {
virtual void connect() {
cout << "BaseConnection::connect" << endl;
}
};
struct BetterConnection : public BaseConnection {
void connect() override {
cout << "BetterConnection::connect" << endl;
}
};
struct BestConnection : public BetterConnection {
void connect() override {
cout << "BestConnection::connect" << endl;
}
};
class X {
public:
X(std::unique_ptr<BaseConnection> connection)
: connection_(std::move(connection))
{
connection_->connect();
}
private:
std::unique_ptr<BaseConnection> connection_;
};
int main() {
X(std::make_unique<BaseConnection>());
X(std::make_unique<BetterConnection>());
X(std::make_unique<BestConnection>());
}
如果Connection
类型具有不同的方法,因为它们确实在执行不同的操作,那么继承是否是正确的抽象使用就引出了一个问题。
也许您可以添加一个您覆盖的虚拟方法,以便为每个派生的 '做正确的事情' Connection
。那么高层类只需要调用这一个方法,不用强制转换就可以了。
一般来说,如果你发现自己不得不dynamic_cast
在运行时执行类型检查,这可能意味着接口不是为多态而设计的。我会重新考虑你的对象之间的接口,并尝试看看是否有一种方法可以获得你想要的东西而不必向上转换。
编辑:使用类型特征
根据您的评论,与我提供的原始答案相比,您可能需要对更高级别的对象进行更多的自定义。从本质上讲,我认为您正在尝试做的是Connection
您正在管理的底层类型的案例,并提供更高级别功能的不同实现。
执行此操作(以及 STL 如何执行此操作)的常用方法是通过具有类型特征的运算符重载。为了说明,从描述底层连接对象特征的几种类型开始。
struct base_connection_tag {};
struct better_connection_tag : public base_connection_tag {};
struct best_connection_tag : public better_connection_tag {};
然后我们可以将它们添加到Connection
类中。
struct BaseConnection {
virtual void a() {
cout << "BaseConnection::a()" << endl;
}
using connection_category = base_connection_tag;
};
struct BetterConnection : public BaseConnection {
virtual void b() {
cout << "BetterConnection::b()" << endl;
}
using connection_category = better_connection_tag;
};
struct BestConnection : public BetterConnection {
virtual void c() {
cout << "BestConnection::c" << endl;
}
using connection_category = best_connection_tag;
};
按照约定,connection_traits
回显Connection
类的嵌套 typedef
template <typename ConnectionT>
struct connection_traits {
using connection_category = typename ConnectionT::connection_category;
};
最后,我们可以使用运算符重载来决定在某个更高级别的类(或多个类)中调用哪个实现,使用如下模式:
template <typename T>
class Dispatch
{
public:
Dispatch(std::unique_ptr<T> connection)
: connection_(std::move(connection))
{}
void operator()() {
connect(typename connection_traits<T>::connection_category());
}
private:
void connect(base_connection_tag) {
connection_->a();
}
void connect(better_connection_tag) {
connection_->b();
}
void connect(best_connection_tag) {
connection_->c();
}
std::unique_ptr<T> connection_;
};
()
调用运算符时,Dispatch
该类使用底层的 调用其中一种connect
方法。connection_traits
Connection
由于所有类型在编译时都是已知的,因此Dispatch
类知道在编译期间要调用哪个基方法。不需要dynamic_cast
确定持有哪种类型。
虽然我只使用一个模板类来实现更高阶的功能,但您可以使用多个非模板类来做同样的事情,connection_traits
在每个类中使用函数参数重载来启用/禁用功能。
推荐阅读
- c# - 从 .json URL 获取数据 [Xamarin]
- python - 用户身份验证 is_active 错误标志在 Django 中不起作用
- jpa - 通过花时间从表中获取记录来休眠 jpa 订单
- macos - chmod 755 所有 Mach-O 可执行文件 - macosx
- swift - 添加约束后,放置在谷歌地图上的按钮消失 Xcode ios Swift
- apache - .htaccess 中的子域访问问题
- c# - 十进制是四倍精度吗?
- git - 在没有本地克隆的情况下更改 git 存储库远程的分支头?
- python - 使用带有地理定位的请求问题使用 python 进行 Webscraping
- python - Django 重命名/重构自定义用户模型