【问题标题】:Should i use dynamic_cast here?我应该在这里使用 dynamic_cast 吗?
【发布时间】:2021-01-26 12:50:41
【问题描述】:

好吧,我有下一个代码:

#include <type_traits>
#include <iostream>
#include <string>
#include <list>
#include <functional>

class base_main
{
public:
    virtual ~base_main()
    {
    }
    // some methods
};

class base_1 : virtual public base_main
{
    // some methods
};

class base_2 : virtual public base_main
{
    // some methods
};

class base_3 : virtual public base_main
{
    // some methods
};

class object : public base_1, public base_2, public base_3
{
    // some methods
};

// in other *hpp file

class object_controller_listener
{
public:
    virtual void object_created( base_main* o )
    {
        // well, i want to work only with base_1 and base_2 interfaces, but not with base_3, and also i don't want to know something about object class in this *hpp
        // is it good code design?
        auto* xxx = dynamic_cast<base_1*>( o );
    }
};

class objects_controller
{
    void create()
    {
        std::unique_ptr<object> obj;

        // ...
        
        for( auto listener : m_listeners )
            listener->object_created( obj.get()  );
    }

    std::list<object_controller_listener*> m_listeners;
};

int main()
{

}

问题是 - 我怎样才能只使用 base_1 和 base_2 接口?我应该为它们创建两个单独的侦听器,并在 create() 函数中发送两个事件,还是应该使用 dynamic_cast 进行向下转换并在 create() 函数中只发送一个事件?这是好的代码设计还是感觉像代码味道?

更新:

例如:base_1 - 是render_base 类,它包含渲染数据,并具有设置和获取该数据的功能 base_2 - 碰撞器基类,它包含对撞机数据,并具有设置和获取该数据的功能 base_3 是物理基类和 object 是所有这些类的继承。当我只想使用渲染类时,我使用创建事件,该事件仅将 render_base 类发送到 render_system,它仅适用于渲染对象并真正使用多态性。但是,如果我想在其他地方使用对撞机和物理对象,而不了解渲染 - 我如何在基类中使用多态性?

【问题讨论】:

  • 视情况而定....
  • 为什么要限制base_main 的哪些子类要使用?您选择它作为一个可以扩展的接口,然后将其限制为您接受的类的子集。
  • if (xxx != nullptr) {xxx-&gt;some_method_of_base1();}。使用dynamic_cast 检查对象是否提供接口意味着您需要在尝试使用特定于每个派生类的成员函数之前对每个派生类型进行硬编码检查。不这样做会产生未定义的行为。尽管在某些情况下dynamic_cast 是必要的,但通常最好专注于使用多态性(在您的情况下,给base_main 一组虚拟成员函数,这些函数可以由派生类根据需要使用和覆盖)代替诉诸dynamic_cast
  • @Kaldrr 例如: base_1 - 是 render_base 类,包含渲染数据,并具有设置和获取此数据的功能 base_2 - 包含对撞机数据的对撞机基类,并具有设置和获取功能此数据库_3 是物理基类,对象是所有这些类的继承。当我只想使用渲染类时,我使用创建事件,该事件仅将渲染基类发送到渲染系统,它仅适用于渲染对象并真正使用多态性。
  • 但是如果我想在其他地方使用对撞机和物理对象,而不了解渲染 - 我如何在基类中使用多态性? @彼得

标签: c++ oop design-patterns


【解决方案1】:

很难说应该选择哪种设计,因为这在很大程度上取决于应用程序的整体结构。

通常,我会避免使用签名为 virtual void object_created( base_main* o ) 的函数,在该函数中动态转换为 base_* 并直接在此函数中处理。因为函数签名是 API 文档的一部分。

所以我会为 base_1base_2 创建不同的函数并调用它们。

如何做到这一点再次取决于整体结构。您可以创建一个辅助函数,将调用转发给其他函数(这只是一个快速实现,可能看起来像这样:

template <typename DestT, typename SrcT, typename T>
void forward_if(SrcT obj, T *o, void (T::*f)(DestT)) noexcept {
  if (auto tmp = dynamic_cast<DestT>(obj); tmp != nullptr) {
    (o->*f)(tmp);
  }
}

然后你可以这样使用它:

#include <iostream>
#include <vector>

class base_main {
public:
  virtual ~base_main() {}
};

class base_1 : virtual public base_main {};

class base_2 : virtual public base_main {};

class base_3 : virtual public base_main {};

class object : public base_1, public base_2, public base_3 {};

template <typename DestT, typename SrcT, typename T>
void forward_if(SrcT obj, T *o, void (T::*f)(DestT)) noexcept {
  if (auto tmp = dynamic_cast<DestT>(obj); tmp != nullptr) {
    (o->*f)(tmp);
  }
}

struct listener_base {
  virtual void object_created(base_main *o) = 0;
};

struct specific_listener : public listener_base {
  void object_created(base_main *o) override {
    forward_if<base_1 *>(o, this, &specific_listener::object_created);
    forward_if<base_2 *>(o, this, &specific_listener::object_created);
  }

  void object_created(base_1 *o) {
    std::cout << "object created base_1" << std::endl;
  }

  void object_created(base_2 *o) {
    std::cout << "object created base_2" << std::endl;
  }
};

int main() {
  std::vector<listener_base *> listeners;
  listeners.push_back(new specific_listener());
  object o;

  for (auto listener : listeners) {
    listener->object_created(&o);
  }

  return 0;
}

【讨论】:

  • g00d,但是为什么不直接将对象发送到 base_1 和 base_2 的 object_created 呢?在我的情况下 - 监听器是观察者模式,如果我将在主监听器中为每个 base_class 创建一个函数 - 每个监听器对象都会知道这个基类,即使它们不需要。但是,我们可以将mega_listener分成小监听器,每个监听器都会为一个基类实现object_created,然后我们应该为每个基类调用object_created->这对我来说不方便,尤其是当我们需要合并基类时.
  • dynamic_cast 的想法似乎很有用,因为我们只在 listener 对象中 dispatch 基类,并且只调用 object_created 一次。
  • @ov3rdr1v337 我添加了listener_base 类,以阐明用法。
  • @ov3rdr1v337 在您的代码评论中,您说i want to work only with base_1 and base_2,所以您问如何处理那个听众中的base_1base_2if i will create a function in main listener […] every listener object will know about this base classes 不,您只会将这些类添加到侦听器应处理的特定侦听器中吗? But, we can divide it mega_listener to small listeners, each of them will realize object_created for only one base class, …我不明白那部分。 The idea with dynamic_cast seems useful, …是的。
猜你喜欢
  • 2014-04-05
  • 1970-01-01
  • 1970-01-01
  • 2011-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-11
相关资源
最近更新 更多