有没有办法在超类构造函数中知道调用对象的子类? C++
Is there a way to know in the superclass constructor the subclass of the calling object? C++
假设我有类似的东西:
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) {}
};
不幸的是,dynamic_cast 无法识别它是一个 B 对象(实例化时)。这听起来合乎逻辑(因为在调用超类中的构造函数之前调用了子类中的构造函数。有没有办法实现这样的功能?
您的设计看起来很复杂,几乎可以肯定可以在不需要知道它的派生基础的情况下实现。
然而,用 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);
}
输出:
not called from derived B
called from derived B
注意:我注意到您正在使用静态变量进行计数。请注意,每个模板实例化都会有自己的静态变量!
首先,要使 dynamic_cast
工作,您需要在基 class 中至少有一个虚拟方法(通常析构函数是虚拟的,因为您经常出于其他原因需要它)。
其次,在构造 A
子对象时,在 A
的构造函数中,所有虚函数都将引用 A
的 class 定义(即使您在fact 将 A
构造为 B
) 和 dynamic_cast
的子对象会说 this
不是类型 B
.
这是因为对象不是类型B
的对象,直到B
的[=44]完成=] 构造函数,因此所有试图确定它是否是类型 B
的检查都会说它不是,因为该对象实际上不是类型 B
(还)。
我希望这能为您消除一些困惑。
编辑:
我刚刚注意到你问题的最后一段,你反而要求解决这个问题。
如果不进一步了解该问题,将很难真正帮助您实现此功能。我的建议是不要那样做,也许完全使用另一个 class 来将 id 分配给您的对象,而不是试图让 A
这样做。
如果它确实必须在 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){}
};
如果 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>())
{
}
};
另一种方法是 AMA 建议的基于模板的编程。
但是,尽管模板是 C++ 中的强大功能,但它们也有自己的问题。它们不能在 cpp 文件中实现。它们会导致代码膨胀,因为较大的代码大小会导致更高的指令缓存未命中率,从而影响性能。
所以我认为这种方法优于 RTTI
,后者具有 运行 时间性能影响,并且您必须在基础 class 中声明一个虚函数,以及基于模板的方法.
这似乎工作得很好:
#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);
}
感谢 AMA。任何事情都可以在 C++ 中完成
假设我有类似的东西:
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) {}
};
不幸的是,dynamic_cast 无法识别它是一个 B 对象(实例化时)。这听起来合乎逻辑(因为在调用超类中的构造函数之前调用了子类中的构造函数。有没有办法实现这样的功能?
您的设计看起来很复杂,几乎可以肯定可以在不需要知道它的派生基础的情况下实现。
然而,用 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);
}
输出:
not called from derived B
called from derived B
注意:我注意到您正在使用静态变量进行计数。请注意,每个模板实例化都会有自己的静态变量!
首先,要使 dynamic_cast
工作,您需要在基 class 中至少有一个虚拟方法(通常析构函数是虚拟的,因为您经常出于其他原因需要它)。
其次,在构造 A
子对象时,在 A
的构造函数中,所有虚函数都将引用 A
的 class 定义(即使您在fact 将 A
构造为 B
) 和 dynamic_cast
的子对象会说 this
不是类型 B
.
这是因为对象不是类型B
的对象,直到B
的[=44]完成=] 构造函数,因此所有试图确定它是否是类型 B
的检查都会说它不是,因为该对象实际上不是类型 B
(还)。
我希望这能为您消除一些困惑。
编辑:
我刚刚注意到你问题的最后一段,你反而要求解决这个问题。
如果不进一步了解该问题,将很难真正帮助您实现此功能。我的建议是不要那样做,也许完全使用另一个 class 来将 id 分配给您的对象,而不是试图让 A
这样做。
如果它确实必须在 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){}
};
如果 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>())
{
}
};
另一种方法是 AMA 建议的基于模板的编程。 但是,尽管模板是 C++ 中的强大功能,但它们也有自己的问题。它们不能在 cpp 文件中实现。它们会导致代码膨胀,因为较大的代码大小会导致更高的指令缓存未命中率,从而影响性能。
所以我认为这种方法优于 RTTI
,后者具有 运行 时间性能影响,并且您必须在基础 class 中声明一个虚函数,以及基于模板的方法.
这似乎工作得很好:
#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);
}
感谢 AMA。任何事情都可以在 C++ 中完成