Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/153.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 派生类中的sizeof(*this)和decltype(*this)_C++_C++11_Inheritance_Sizeof_Decltype - Fatal编程技术网

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
}