Class 在 C++ 中的 Class 内部:它很重吗?
Class Inside a Class in C++: Is that heavy?
假设我做了类似的事情:
class A
{
public:
class B
{
public:
void SomeFunction1() const;
using atype = A;
};
using btype = B;
void SomeFunction2() const;
private:
B b;
};
然后我创建一个 class A 的实例并复制它:
A a;
A acopy = a;
这是否使 class 变得沉重,我的意思是,后台发生了什么?我希望每次声明 class A 的实例时,C++ 都不会真的从字面上“考虑” class B 的定义,我想在后台发生的事情是 class B将被视为名为 A 的名称空间下的新定义,因此定义了 A::B。我的问题是在 class 中定义 class(A 中的 B)是否会在声明或复制 class A 时产生任何开销,或者它是否被完全视为 B 是在外部定义的?
谢谢大家:)
我个人的看法是:
- 即使源代码折叠,阅读起来也很沉重。
- 它通常没用:
B
class 大多数时候可以与 A
class 完全合并。
- 即使
B
如果 A
需要,并且 从来没有 在其他地方使用过,它可以在 A
之外定义。构造函数是您“强制”某些关联的朋友 - 例如,删除默认构造函数。
- 在内部或外部
A
,它不会改变任何有关深层复制的内容。
- 如果您需要直接通过
A
公开 B
,这是一个设计缺陷 - 将这些功能从 B
移至 A
。
所以我真的看不出,除了“保留”头文件之外,通过在 class...
中声明 class 可以“获得”什么
就我而言,我更喜欢:
- 如果不需要公开,请使用 PIMPL idiom“隐藏”
B
。
- 永久移动需要在
A
中公开的所有内容(移动源代码),或者对 PIMPL 对象使用包装器。
- 如果您需要 replace/share
B
实例,请将 PIMPL 对象的 unique_ptr
更改为 shared_ptr
。注意深拷贝。
它通常效率更高,如果需要,可以完美隐藏 B
代码(例如,甚至不需要从 DLL 导出它),您不会重新编译 B
所有这些都是在更改 A
时进行的,您甚至可以在不破坏 A
API.
的情况下更改 B
的工作方式
C++ 是一种编译语言。我们通常假设用于编译 C++ 源代码的计算机足够强大。编译器在编译可执行文件的过程中可以合理地使用千兆字节。
所以当我们说某些东西“很重”时,我们通常是指它在运行时很重。 C++ 程序的运行时环境很可能是咖啡机或冰箱。
嵌套范围内的名称查找之类的事情是纯粹的 compile-time 操作,不会对运行时产生任何影响。现代编译器足够聪明,可以在此处内联默认方法,因此调用成员时没有运行时开销。
两种可能性(B 作为嵌套 class 和 B 作为外部 class)将产生完全相同的性能。
事实上,编译器在两种情况下都会生成相同的汇编代码。
B 作为外部 class:
https://godbolt.org/z/7voYGd6Mf
B 嵌套 class:
https://godbolt.org/z/731dPdrqo
B 是 A 的成员。因此它驻留在 A 的内存布局中,每次 constructor/copy A 时都会调用 B 的构造函数。
引入的开销取决于 B 实现,但在两种情况下都是相同的(B 嵌套和外部 class),
所以我是问这个问题的人,我已经测试了@david dragilev 在 Google Benchmark 上实现的内容。结果附上,确实差别很小。我不能说外部版本更快,但理论上(基于数字)是。
假设我做了类似的事情:
class A
{
public:
class B
{
public:
void SomeFunction1() const;
using atype = A;
};
using btype = B;
void SomeFunction2() const;
private:
B b;
};
然后我创建一个 class A 的实例并复制它:
A a;
A acopy = a;
这是否使 class 变得沉重,我的意思是,后台发生了什么?我希望每次声明 class A 的实例时,C++ 都不会真的从字面上“考虑” class B 的定义,我想在后台发生的事情是 class B将被视为名为 A 的名称空间下的新定义,因此定义了 A::B。我的问题是在 class 中定义 class(A 中的 B)是否会在声明或复制 class A 时产生任何开销,或者它是否被完全视为 B 是在外部定义的?
谢谢大家:)
我个人的看法是:
- 即使源代码折叠,阅读起来也很沉重。
- 它通常没用:
B
class 大多数时候可以与A
class 完全合并。 - 即使
B
如果A
需要,并且 从来没有 在其他地方使用过,它可以在A
之外定义。构造函数是您“强制”某些关联的朋友 - 例如,删除默认构造函数。 - 在内部或外部
A
,它不会改变任何有关深层复制的内容。 - 如果您需要直接通过
A
公开B
,这是一个设计缺陷 - 将这些功能从B
移至A
。
所以我真的看不出,除了“保留”头文件之外,通过在 class...
中声明 class 可以“获得”什么就我而言,我更喜欢:
- 如果不需要公开,请使用 PIMPL idiom“隐藏”
B
。 - 永久移动需要在
A
中公开的所有内容(移动源代码),或者对 PIMPL 对象使用包装器。 - 如果您需要 replace/share
B
实例,请将 PIMPL 对象的unique_ptr
更改为shared_ptr
。注意深拷贝。
它通常效率更高,如果需要,可以完美隐藏 B
代码(例如,甚至不需要从 DLL 导出它),您不会重新编译 B
所有这些都是在更改 A
时进行的,您甚至可以在不破坏 A
API.
B
的工作方式
C++ 是一种编译语言。我们通常假设用于编译 C++ 源代码的计算机足够强大。编译器在编译可执行文件的过程中可以合理地使用千兆字节。
所以当我们说某些东西“很重”时,我们通常是指它在运行时很重。 C++ 程序的运行时环境很可能是咖啡机或冰箱。
嵌套范围内的名称查找之类的事情是纯粹的 compile-time 操作,不会对运行时产生任何影响。现代编译器足够聪明,可以在此处内联默认方法,因此调用成员时没有运行时开销。
两种可能性(B 作为嵌套 class 和 B 作为外部 class)将产生完全相同的性能。
事实上,编译器在两种情况下都会生成相同的汇编代码。
B 作为外部 class:
https://godbolt.org/z/7voYGd6Mf
B 嵌套 class:
https://godbolt.org/z/731dPdrqo
B 是 A 的成员。因此它驻留在 A 的内存布局中,每次 constructor/copy A 时都会调用 B 的构造函数。 引入的开销取决于 B 实现,但在两种情况下都是相同的(B 嵌套和外部 class),
所以我是问这个问题的人,我已经测试了@david dragilev 在 Google Benchmark 上实现的内容。结果附上,确实差别很小。我不能说外部版本更快,但理论上(基于数字)是。