dynamic_cast的继承和使用

发布于 2024-09-06 10:19:10 字数 1096 浏览 7 评论 0原文

假设我有如下 3 个类(因为这是一个示例,它将无法编译!):

class Base
{
public:
   Base(){}
   virtual ~Base(){}
   virtual void DoSomething() = 0;
   virtual void DoSomethingElse() = 0;
};

class Derived1
{
public:
   Derived1(){}
   virtual ~Derived1(){}
   virtual void DoSomething(){ ... }
   virtual void DoSomethingElse(){ ... }
   virtual void SpecialD1DoSomething{ ... }
};

class Derived2
{
public:
   Derived2(){}
   virtual ~Derived2(){}
   virtual void DoSomething(){ ... }
   virtual void DoSomethingElse(){ ... }
   virtual void SpecialD2DoSomething{ ... }
};

我想根据某些直到运行时才可用的设置来创建 Derived1 或 Derived2 的实例。

由于我在运行时之前无法确定派生类型,那么您认为以下做法是不好的做法吗?...

class X
{
public:
   ....

   void GetConfigurationValue()
   {
      ....
      // Get configuration setting, I need a "Derived1"
      b = new Derived1();

      // Now I want to call the special DoSomething for Derived1
      (dynamic_cast<Derived1*>(b))->SpecialD1DoSomething();      
   }
private:
   Base* b;
};

我通常读到 Dynamic_cast 的使用不好,但正如我所说,我不知道 在运行时之前要创建哪种类型。请帮忙!

Suppose I have 3 classes as follows (as this is an example, it will not compile!):

class Base
{
public:
   Base(){}
   virtual ~Base(){}
   virtual void DoSomething() = 0;
   virtual void DoSomethingElse() = 0;
};

class Derived1
{
public:
   Derived1(){}
   virtual ~Derived1(){}
   virtual void DoSomething(){ ... }
   virtual void DoSomethingElse(){ ... }
   virtual void SpecialD1DoSomething{ ... }
};

class Derived2
{
public:
   Derived2(){}
   virtual ~Derived2(){}
   virtual void DoSomething(){ ... }
   virtual void DoSomethingElse(){ ... }
   virtual void SpecialD2DoSomething{ ... }
};

I want to create an instance of Derived1 or Derived2 depending on some setting that is not available until run-time.

As I cannot determine the derived type until run-time, then do you think the following is bad practice?...

class X
{
public:
   ....

   void GetConfigurationValue()
   {
      ....
      // Get configuration setting, I need a "Derived1"
      b = new Derived1();

      // Now I want to call the special DoSomething for Derived1
      (dynamic_cast<Derived1*>(b))->SpecialD1DoSomething();      
   }
private:
   Base* b;
};

I have generally read that usage of dynamic_cast is bad, but as I said, I don't know
which type to create until run-time. Please help!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

倾其所爱 2024-09-13 10:19:10

为什么不通过将派生指针分配给基指针来延迟“丢弃”一些类型信息的时刻:

void GetConfigurationValue()
{
  // ...
  // Get configuration setting, I need a "Derived1"
  Derived1* d1 = new Derived1();
  b = d1;

  // Now I want to call the special DoSomething for Derived1
  d1->SpecialD1DoSomething();
}

Why not delay the moment at which you "throw away" some if the type information by assigning a pointer to derived to a pointer to base:

void GetConfigurationValue()
{
  // ...
  // Get configuration setting, I need a "Derived1"
  Derived1* d1 = new Derived1();
  b = d1;

  // Now I want to call the special DoSomething for Derived1
  d1->SpecialD1DoSomething();
}
两人的回忆 2024-09-13 10:19:10

虚函数的要点在于,一旦拥有正确类型的对象,您就可以调用正确的函数,而无需知道该对象是哪个派生类——您只需调用虚函数,它就会执行以下操作:正确的事情。

当您有一个派生类定义了基类中存在的不同内容时,您只需要一个dynamic_cast并且您需要/想要考虑到额外的东西。

例如:

struct Base { 
    virtual void do_something() {}
};

struct Derived : Base { 
    virtual void do_something() {} // override dosomething
    virtual void do_something_else() {} // add a new function
};

现在,如果您只想调用do_something(),则完全不需要dynamic_cast。例如,您可以拥有一个 Base * 集合,只需对每个集合调用 do_something() 即可,而不用关心该对象是否真的是一个 基础派生

当/如果您有一个Base *,并且您想要调用do_something_else()那么您可以使用dynamic_cast 来确定对象本身是否真的是一个 Derived 以便您可以调用它。

The point of virtual functions is that once you have the right kind of object, you can call the right function without knowing which derived class this object is -- you just call the virtual function, and it does the right thing.

You only need a dynamic_cast when you have a derived class that defines something different that's not present in the base class, and you need/want to take the extra something into account.

For example:

struct Base { 
    virtual void do_something() {}
};

struct Derived : Base { 
    virtual void do_something() {} // override dosomething
    virtual void do_something_else() {} // add a new function
};

Now, if you just want to call do_something(), a dynamic_cast is completely unnecessary. For example, you can have a collection of Base *, and just invoke do_something() on every one, without paying any attention to whether the object is really a Base or a Derived.

When/if you have a Base *, and you want to invoke do_something_else(), then you can use a dynamic_cast to figure out whether the object itself is really a Derived so you can invoke that.

迷你仙 2024-09-13 10:19:10

使用dynamic_cast本身并不是一个坏习惯。不恰当地使用它是不好的做法,即在并不真正需要的地方使用它。

以这种方式使用它也是一个不好的做法:

(dynamic_cast<Derived1*>(b))->SpecialD1DoSomething();  

原因:dynamic_cast(b) 可能返回 NULL。

使用dynamic_cast时,您必须格外小心,因为不能保证b实际上是Derived1类型而不是Derived2类型:

void GenericFunction(Base* p)
{
    (dynamic_cast<Derived1*>(b))->SpecialD1DoSomething();
}

void InitiallyImplementedFunction()
{
   Derived1 d1;
   GenericFunction(&d1); // OK... But not for long. 
   // Especially, if implementation of GenericFunction is in another library
   // with not source code available to even see its implementation 
   // -- just headers
}    

void SomeOtherFunctionProbablyInAnotherUnitOfCompilation()
{
   Derived2 d2;
   GenericFunction(&d2); // oops!
}

您必须检查dynamic_cast是否确实成功。有两种方法可以做到这一点:在演员之前和之后检查。在转换之前,您可以通过 RTTI 检查您尝试转换的指针是否实际上是您期望的指针:

if (typeid(b) == typeid(Derived1*))
{
   // in this case it's safe to call the function right 
   // away without additional checks
   dynamic_cast<Derived1*>(b)->SpecialD1DoSomething();
}
else
{
  // do something else, like try to cast to Derived2 and then call
  // Derived2::SpecialD2DoSomething() in a similar fashion
}

事后检查实际上更简单一些:

Derived1* d1 = dynamic_cast<Derived1*>(b);
if (d1 != NULL)
{
   d1->SpecialD1DoSomething();
}

我还想说,尝试在同时保存打字是一种不好的做法用 C++ 编程。 C++ 中的许多功能似乎完全可以用更短的类型来表示(即让您感觉“NULL 永远不会在这里发生”),但事后调试起来却很痛苦。 ;)

Using dynamic_cast is not bad practice per se. It's bad practice to use it inappropriately, i.e. where it's not really needed.

It's also a bad practice to use it this way:

(dynamic_cast<Derived1*>(b))->SpecialD1DoSomething();  

Reason: dynamic_cast(b) may return NULL.

When using dynamic_cast, you have to be extra careful, because it's not guaranteed, that b is actually of type Derived1 and not Derived2:

void GenericFunction(Base* p)
{
    (dynamic_cast<Derived1*>(b))->SpecialD1DoSomething();
}

void InitiallyImplementedFunction()
{
   Derived1 d1;
   GenericFunction(&d1); // OK... But not for long. 
   // Especially, if implementation of GenericFunction is in another library
   // with not source code available to even see its implementation 
   // -- just headers
}    

void SomeOtherFunctionProbablyInAnotherUnitOfCompilation()
{
   Derived2 d2;
   GenericFunction(&d2); // oops!
}

You have to check if dynamic_cast is actually successful. There are two ways of doing it: checking it before and after the cast. Before the cast you can check if the pointer you're trying to cast is actually the one you expect via RTTI:

if (typeid(b) == typeid(Derived1*))
{
   // in this case it's safe to call the function right 
   // away without additional checks
   dynamic_cast<Derived1*>(b)->SpecialD1DoSomething();
}
else
{
  // do something else, like try to cast to Derived2 and then call
  // Derived2::SpecialD2DoSomething() in a similar fashion
}

Checking it post-factum is actually a bit simpler:

Derived1* d1 = dynamic_cast<Derived1*>(b);
if (d1 != NULL)
{
   d1->SpecialD1DoSomething();
}

I'd also say it's a bad practice to try and save typing while programming in C++. There are many features in C++ than seem to be completely fine to be typed shorter (i.e. makes you feel 'that NULL will never happen here'), but turn out to be a pain in the ass to debug afterwards. ;)

静若繁花 2024-09-13 10:19:10

您可能需要考虑避免使用dynamic_cast的其他一些事情

来自Effective C++(第三版) - Item 35 虚拟函数的替代方案 -

  1. 通过非虚拟接口(NVI)的“模板方法模式”。使用公共方法“包装器”将虚拟函数设为私有/受保护 - 允许您在虚拟方法之前和之后强制执行一些其他工作流程。
  2. 通过函数指针的“策略模式”。传入额外的方法作为函数指针。
  3. 通过 tr1::function 的“策略模式”。与 2. 类似,但您可以为整个班级提供各种选项
  4. “策略模式”经典。将策略与主类分开 - 将虚拟函数推入另一个层次结构。

Some other things you might like to consider to avoid the use of dynamic_cast

From Effective C++ (Third Edition) - Item 35 Alternatives to virtual functions -

  1. 'Template Method pattern' via Non-Vitual Interface (NVI). Making the virtual functions private/protected with a public method 'wrapper' - allows you to enforce some other workflow of stuff to do before and after the virtual method.
  2. 'Strategy pattern' via function pointers. Pass in the extra method as a function pointer.
  3. 'Strategy pattern' via tr1::function. similar to 2. but you could provide whole classes with various options
  4. 'Strategy pattern' classic. Seperate Strategy from main class - push the virtual functions into another hierarchy.
骄兵必败 2024-09-13 10:19:10

有一个名为 工厂模式 的模式适合这种情况。这允许您根据某些输入参数返回正确类的实例。

享受!

There is a pattern named Factory Pattern that would fit this scenario. This allows you to return an instance of the correct class based on some input parameter.

Enjoy!

马蹄踏│碎落叶 2024-09-13 10:19:10

有什么问题:

Base * b;
if( some_condition ) {
   b = new Derived1;
}
else {
   b = new Derived2;
}

if ( Derived2 * d2 = dynamic_cast <Derived2 *>( b ) ) {
    d2->SpecialD2DoSomething();
}

或者我错过了什么?

我可以建议,当你发布这样的问题时,你(和其他人)将你的类命名为 A、B、C 等,并将你的函数命名为 f1()、f2() 等。这让回答你问题的人变得更容易。

What is wrong with:

Base * b;
if( some_condition ) {
   b = new Derived1;
}
else {
   b = new Derived2;
}

if ( Derived2 * d2 = dynamic_cast <Derived2 *>( b ) ) {
    d2->SpecialD2DoSomething();
}

Or am I missing something?

And can OI suggest that when posting questions like this you (and others) name your classes A, B, C etc. and your functions things like f1(), f2() etc. It makes life a lot easier for people answering your questions.

黯然#的苍凉 2024-09-13 10:19:10

避免dynamic_cast的一种方法是使用一个虚拟蹦床函数“SpecialDoSomething”,其派生多态实现调用该特定派生类的“SpecialDxDoSomething()”,该函数可以是您想要的任何非基类名称。它甚至可以调用多个函数。

One way to avoid dynamic_cast is to have a virtual trampoline function "SpecialDoSomething" whose derived polymorphic implementation calls that particular derived class's "SpecialDxDoSomething()" which can be whatever non-base class name you desire. It can even call more than one function.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文