C++ 派生类中的sizeof(*this)和decltype(*this)
假设有几个类:C++ 派生类中的sizeof(*this)和decltype(*this),c++,c++11,inheritance,sizeof,decltype,C++,C++11,Inheritance,Sizeof,Decltype,假设有几个类: struct A { int a; virtual size_t GetMemoryUsage() const { return sizeof(*this); } }; struct B : public A { int b; }; 而且可能会有更深层次的继承 我想要的是有一个方法返回一个对象在内存中占用的字节数,GetMemoryUsage()。通常可以通过使用sizeof(*this)来实现。问题是(至少是AFAIU),我必须重写每个派生类中的方
struct A {
int a;
virtual size_t GetMemoryUsage() const {
return sizeof(*this);
}
};
struct B : public A {
int b;
};
而且可能会有更深层次的继承
我想要的是有一个方法返回一个对象在内存中占用的字节数,GetMemoryUsage()
。通常可以通过使用sizeof(*this)
来实现。问题是(至少是AFAIU),我必须重写每个派生类中的方法,并实际复制粘贴其主体。我不喜欢重复的代码:)
我说得对吗?如何使
sizeof(*this)
和decltype(*this)
通过仅从基类的方法调用子类来返回我想要的子类?有更优雅的解决方案吗?您不必手动为每个派生类实现GetMemoryUsage
,只需将其保留为纯虚拟类即可。例如:
struct A
{
virtual ~A() = default;
virtual size_t GetMemoryUsage() const noexcept = 0;
};
struct B : A
{
int b;
};
但是,在创建对象时,必须实现该功能。您可以使用一个工厂函数来实现这一点,该工厂函数使用纯虚拟的通用实现来“装饰”类:
// Can alternatively be defined inside function template create.
template<class T>
struct GetMemoryUsageImpl : T
{
using T::T;
size_t GetMemoryUsage() const noexcept final {
return sizeof(T);
}
};
template<class T, class... Args>
std::unique_ptr<T> create(Args&&... args) {
return std::unique_ptr<T>(new GetMemoryUsageImpl<T>(std::forward<Args>(args)...));
}
//也可以在函数模板创建中定义。
模板
结构GetMemorySageImpl:T
{
使用T::T;
大小\u t GetMemoryUsage()常量不例外最终{
返回大小f(T);
}
};
模板
std::unique_ptr create(Args&&…Args){
return std::unique_ptr(新的GetMemoryUsageImpl(std::forward)这是@Maxim解决方案的一个非常通用的版本
template<class B0, template<class...>class... Z>
struct TemplateFold {
using type=B0;
};
template<class B0, template<class...>class... Z>
using TemplateFold_t = typename TemplateFold<B0, Z...>::type;
template<class B0, template<class...>class Z0, template<class...>class... Z>
struct TemplateFold<B0, Z0, Z...>
{
using type=Z0< TemplateFold_t<B0, Z...> >;
};
struct ExposeTrivial {
protected:
~ExposeTrivial() {}
};
template<class D, class B0=ExposeTrivial, class...Bases>
struct Expose:B0, Bases... {
// is a template because D isn't a real type when this class is instantiated:
template<class T>
using MakeConcreteType = TemplateFold_t< T, std::conditional_t< std::is_same<B0,ExposeTrivial>{}, T, B0 >::template Implement, Bases::template Implement... >;
template<class...Args>
static std::unique_ptr<D> create( Args&&... args ) {
using ConcreteType = MakeConcreteType<D>;
return std::unique_ptr<D>( new ConcreteType( std::forward<Args>(args)... ) );
}
protected:
~Expose() {}
};
// expose one thing:
struct ExposeMemoryUsage:Expose<ExposeMemoryUsage> {
virtual std::size_t GetMemoryUsage() const noexcept = 0;
template<class B>
struct Implement:B {
using B::B;
std::size_t GetMemoryUsage() const noexcept override final {
return sizeof(*this);
}
};
protected:
~ExposeMemoryUsage() {}
};
// expose a different thing:
struct ExposeAlignment:Expose<ExposeAlignment>{
virtual std::size_t GetAlignment() const noexcept = 0;
template<class B>
struct Implement:B {
using B::B;
std::size_t GetAlignment() const noexcept final override {
return alignof(decltype(*this));
}
};
};
// Expose two things:
struct Bob : Expose<Bob, ExposeMemoryUsage, ExposeAlignment> {
int x;
Bob( int v ): x(v) {}
virtual ~Bob() {}
};
int main() {
std::unique_ptr<Bob> ptr = Bob::create(7);
std::cout << ptr->x << " size:" << ptr->GetMemoryUsage() << " align:" << ptr->GetAlignment() << "\n";
// Bob b; // does not compile
}
模板
结构模板折叠{
使用类型=B0;
};
模板
使用TemplateFold\u t=typename TemplateFold::type;
模板
结构模板折叠
{
使用类型=Z0;
};
结构暴露{
受保护的:
~exposetrial(){}
};
模板
结构暴露:B0,基{
//是模板,因为实例化此类时D不是实类型:
模板
使用MakeConcreteType=TemplateFold\u t::模板实现,基::模板实现…>;
模板
静态std::unique_ptr create(Args&&…Args){
使用ConcreteType=MakeConcreteType;
返回std::unique_ptr(新混凝土类型)(std::forward
如何使用:
创建一个公开类型。它应该有一个纯虚拟成员和一个模板实现类(给定一个从公开类型派生的类)实现该纯虚拟成员
它应该继承自Expose
(CRTP)来为您编写静态::create
方法
如果要从其他Expose
类型继承(即,组成两个需要知道具体类型的独立的Expose接口),请改为从Expose
继承。不要从OtherExposeType
或另一个exposetype>独立继承
如果执行此操作,将不会拾取工具模板
我可以改进这一点,以便在您和您的基础中检测实现模板,但这比我现在要做的更多。CRTP以避免重复代码(大小).Visitor可能对派生类型有所帮助。我宁愿在实际对象上使用sizeof
,也不愿为其设置函数。通常sizeof(*这)
不是衡量(总)内存使用量的好方法。请尝试使用例如sizeof(一些字符串)
或sizeof(一些向量)
我同意DeiDei的观点,没有代码复制比没有代码复制更好。关于sizeof(B)
获取B
实例的大小有什么不对?@DeiDei我没有实际对象,只有基类类型的指针。唯一的方法是“询问”对象。很好的解决方案。create
也可以是GetMemoryUsageImpl
@Slava的静态方法,如果你喜欢打字,它可以。@Alexey-levenable-ATL-library就是基于这个习惯用法的。我的意思是,这是一个非常古老的习惯用法。create
可以是a
的静态方法。请注意,放置形式也可以很有用(T*construct(void*data,Args&&…Args)
)如果使用起来很棘手,你甚至可以制作一个框架,允许组成这种虚拟的大多数派生方法。@Yakk AdamNevraumont确实可以。或者std::make_unique(…)
但是太冗长了。这是一个有趣的想法。不过,我认为将接口和实现合并到一个类中不是一个好主意。毕竟,从概念上讲,接口的唯一目的是使一段代码能够处理不同类型的对象,而不知道它们的实现细节,只要它们公开接口e、 接口的用户只需要查看接口定义。只有工厂需要实现。实际上,接口及其实现的分离可能有助于减少编译时间。接口的替代实现可能也不适合此设计。@maxim Firat,您不需要使用实现。但更重要的是,OP希望在最派生的类型上以统一的方式实现接口,而不必重复它们自己。这对我来说是使用虚拟方法作为一种类型擦除,而不是传统的OO契约,在类型擦除中,契约通常包括完整的(通用的、可扩展的)实现。
template<class B0, template<class...>class... Z>
struct TemplateFold {
using type=B0;
};
template<class B0, template<class...>class... Z>
using TemplateFold_t = typename TemplateFold<B0, Z...>::type;
template<class B0, template<class...>class Z0, template<class...>class... Z>
struct TemplateFold<B0, Z0, Z...>
{
using type=Z0< TemplateFold_t<B0, Z...> >;
};
struct ExposeTrivial {
protected:
~ExposeTrivial() {}
};
template<class D, class B0=ExposeTrivial, class...Bases>
struct Expose:B0, Bases... {
// is a template because D isn't a real type when this class is instantiated:
template<class T>
using MakeConcreteType = TemplateFold_t< T, std::conditional_t< std::is_same<B0,ExposeTrivial>{}, T, B0 >::template Implement, Bases::template Implement... >;
template<class...Args>
static std::unique_ptr<D> create( Args&&... args ) {
using ConcreteType = MakeConcreteType<D>;
return std::unique_ptr<D>( new ConcreteType( std::forward<Args>(args)... ) );
}
protected:
~Expose() {}
};
// expose one thing:
struct ExposeMemoryUsage:Expose<ExposeMemoryUsage> {
virtual std::size_t GetMemoryUsage() const noexcept = 0;
template<class B>
struct Implement:B {
using B::B;
std::size_t GetMemoryUsage() const noexcept override final {
return sizeof(*this);
}
};
protected:
~ExposeMemoryUsage() {}
};
// expose a different thing:
struct ExposeAlignment:Expose<ExposeAlignment>{
virtual std::size_t GetAlignment() const noexcept = 0;
template<class B>
struct Implement:B {
using B::B;
std::size_t GetAlignment() const noexcept final override {
return alignof(decltype(*this));
}
};
};
// Expose two things:
struct Bob : Expose<Bob, ExposeMemoryUsage, ExposeAlignment> {
int x;
Bob( int v ): x(v) {}
virtual ~Bob() {}
};
int main() {
std::unique_ptr<Bob> ptr = Bob::create(7);
std::cout << ptr->x << " size:" << ptr->GetMemoryUsage() << " align:" << ptr->GetAlignment() << "\n";
// Bob b; // does not compile
}