简体   繁体   English

有没有办法在超类构造函数中知道调用对象的子类? C++

[英]Is there a way to know in the superclass constructor the subclass of the calling object? C++

Let's suppose I have something like:假设我有类似的东西:

class A 
{
  public:
     A(A* owner)
     {
        m_owner=owner;
        if (dynamic_cast<B*>(this))
        {
          m_id=sm_bid; 
          sm_bid++;
        }
        else
        {
          m_id=sm_aid;
          sm_aid++;
        }
     }
  private:
    int m_id;
    A*  m_owner;
    static int sm_aid;
    static int sm_bid;
};

A::sm_aid=0;
A::sm_bid=0;

class B: public A
{
    B(A* owner) : A(owner) {}
};

Unfortunately, the dynamic_cast is unable to catch that it is a B object (when instantiated).不幸的是,dynamic_cast 无法捕捉到它是一个 B 对象(实例化时)。 This sound logical (since the constructor in the superclass is called before to call the constructor in the subclass. Is there a way to achieve such functionality?这听起来合乎逻辑(因为之前调用了超类中的构造函数来调用子类中的构造函数。有没有办法实现这样的功能?

Your design looks convoluted and almost certainly could be implemented without base needing to know it's derived.您的设计看起来很复杂,几乎可以肯定可以在不需要知道它是派生的情况下实现。

However, it is the holy right to shoot oneself in the foot with C++ :) Therefore, you can try using Curiously Recurring Template Pattern (CRTP) to achieve your goals (polymorphic behavior in the constructor via static polymorphism).但是,用 C++ 打自己的脚是神圣的权利 :) 因此,您可以尝试使用Curiously Recurring Template Pattern (CRTP)来实现您的目标(通过静态多态在构造函数中实现多态行为)。

Here is the quick and dirty sketch :这是快速而肮脏的草图

#include <iostream>
#include <type_traits>

struct Owner
{};

struct B;

template <class Derived>
struct A : public Owner
{
    A(Owner*)
    {
        if (std::is_same<Derived, B>::value)
            std::cout << "called from derived B" << std::endl;
        else
            std::cout << "not called from derived B\n" << std::endl;
    }
};

struct B : public A<B>
{
    B(Owner* owner) : A<B>(owner) {}
};

int main()
{
    A<void> a(nullptr);
    B b(&a);
}

Output:输出:

not called from derived B
called from derived B

Note : I noticed that you are using static variables for counting.注意:我注意到您正在使用静态变量进行计数。 Please note that each template instantiation will have its own static variable!请注意,每个模板实例化都有自己的静态变量!

Firstly, for dynamic_cast to work you need to have at least one virtual method in the base class (usually the destructor is made virtual since you often need that for other reasons).首先, dynamic_cast工作,您需要在基类中至少有一个虚拟方法(通常析构函数是虚拟的,因为您经常出于其他原因需要它)。

Secondly, while constructing the A subobject, in A 's constructor all virtual functions will refer A 's class definition (even if you are in fact constructing A as a subobject of B ) and dynamic_cast will say that this is not a type B .其次,在构造A子对象时,在A的构造函数中,所有虚函数都将引用A的类定义(即使您实际上A构造为B的子对象),并且dynamic_cast会说this不是类型B

This is because the object is not an object of type B until the completion of B 's constructor, and so any and all checks to try to determine if it's a type B will say it's not because the object is in fact not a type B (yet).这是因为在B构造函数完成之前,该对象不是类型B的对象,因此任何和所有尝试确定它是否是类型B都会说这不是因为该对象实际上不是类型B (然而)。

I hope this clears up some confusion for you.我希望这可以为您解决一些困惑。

EDIT:编辑:
I just noticed the last paragraph under your question where you instead ask for a solution to this.我刚刚注意到你问题下的最后一段,你在那里寻求解决方案。

Without knowing more about the problem it will be difficult to really help you with achieving this functionality.如果不了解有关问题的更多信息,将很难真正帮助您实现此功能。 My suggestion is to not do that, perhaps use another class altogether to assign id's to your objects rather than trying to have A do it.我的建议是不要这样做,也许完全使用另一个类来为您的对象分配 id,而不是尝试让A这样做。

If it really must be in A then perhaps this could be of help:如果它真的必须在A那么这可能会有所帮助:

struct A
{
  template <class FinalType>
  A(A* owner, FinalType *){...}//use std::is_same to check if `B` or not
};

struct B : A
{
  B(A * owner):A(owner, this){}
};

If A is an abstract type you can use tag based overloading technique.如果A是抽象类型,则可以使用基于标签的重载技术。

class B;
class C;

class A
{
public:
    template<class Derived>
    struct my_derived
    {
        using type = Derived;
    };

protected:
    explicit A(my_derived<B>)
    {
        // Do whatever you want when derived is B
    }

    explicit A(my_derived<C>)
    {
        // Do whatever you want when derived is C
    }

private:

};

class B : A
{
public:
    B() : A(A::my_derived<B>())
    {
    }
};

class C : A
{
public:
    C() : A(A::my_derived<C>())
    {
    }
};

Also another approach would be template based programming as AMA suggest.另一种方法是 AMA 建议的基于模板的编程。 But Although Templates are powerful feature in C++ they have their own problems.但是尽管模板是 C++ 中的强大功能,但它们也有自己的问题。 They can't be implemented in a cpp file.它们不能在 cpp 文件中实现。 They cause code bloat that can affect performance due to bigger code size that will cause higher rate of instruction cache miss.它们会导致代码膨胀,从而影响性能,因为更大的代码大小会导致更高的指令缓存未命中率。

So I think this approach is better than RTTI that has run time performance impact and you have to declare one virtual function in your base class, and also template based approach.所以我认为这种方法比RTTI更好, RTTI会影响运行时性能,并且您必须在基类中声明一个虚拟函数,以及基于模板的方法。

It seems like this works just fine:看起来这很好用:

#include <iostream>
#include <type_traits>

class Owner
{};

class B;

template <class Derived>
class A : public Owner
{
    public:
    A(Owner* owner)
    {
        m_owner=owner;
        if (std::is_same<Derived, B>::value)
            std::cout << "called from derived B" << std::endl;
        else
            std::cout << "not called from derived B\n" << std::endl;
    }
    private:
    Owner* m_owner;
};

class B : public A<B>
{
    public:
    B(Owner* owner) : A<B>(owner)
    {}
};

int main()
{
    A<void> a(nullptr);
    B b(&a);
}

Kudos to AMA.向 AMA 致敬。 Anything can be done in C++任何事情都可以在 C++ 中完成

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM