#c++ 学习笔记
C++:Trivial、Standard-Layout 和 POD
引用
六类特殊成员函数
默认构造函数,
T::T()
拷贝构造函数,
T::T( (const) (volatile) T&)
移动构造函数,
T::T( (const) (volatile) T&&)
拷贝赋值运算符,
T::operator=( (const) (volatile) T&)
移动赋值运算符,即
T::operator=( (const) (volatile) T&&)
析构函数,
T::~T()
trivial type
没有虚函数或虚基类。
由编译器生成默认的特殊成员函数(缺省或者
=default
形式)。数据成员同样需要满足条件 1 和 2。
空定义或者 =delete都不是Trivial了
可以通过特征萃取工具分别去检测特殊成员函数
#include <type_traits>
#include <iostream>
int main()
{
using namespace std;
cout << is_trivially_default_constructible<Foo>::value << std::endl;
cout << is_trivially_copy_constructible<Foo>::value << std::endl;
cout << is_trivially_move_constructible<Foo>::value << std::endl;
cout << is_trivially_copy_assignable<Foo>::value << std::endl;
cout << is_trivially_move_assignable<Foo>::value << std::endl;
cout << is_trivially_destructible<Foo>::value << std::endl;
}
六个is_trivially_* 的用处:在模板库中,就可以为对应的成员函数为 Trivial 的类型,做单独的特化,比如构造或者析构时可以不用做相应的操作。
TriviallyCopyable
除去默认构造函数, 其他五个都要满足trivial type。
对于满足 TriviallyCopyable 要求的类型,可以安全地使用 C 中 memcpy, memmove 等面向字节操作的函数,以提高程序的性能,而不用担心发生任何有关深浅拷贝的问题。
比如std::copy可以实现优化成 memmove。 std::copy 只适合做不重叠的拷贝或者向前拷贝。重叠的向后拷贝应当交给 std::copy_backward。
注意,自己实现的myCopy 不能被编译器优化成 memmove?编译器擅自将 myCopy 翻译成 memmove 是错误的优化!!!在目标区间和源区间有重叠的时候,memmove 执行的结果和优化前可能是不一致的。
Standard Layout
当类(class 或 struct )同时满足以下几个条件时是标准布局(standard-layout)类型:
没有虚函数或虚基类。
所有非静态数据成员都具有相同的访问说明符(public / protected / private)。
在继承体系中最多只有一个类中有非静态数据成员。
子类中的第一个非静态成员的类型与其基类不同。
第 4 个条件的产生是因为 C++ 允许优化不包含成员基类:
在 C++ 标准中,如果基类没有任何数据成员,基类应不占用空间。所以,C++ 标准允许派生类的第一个成员与基类共享同一地址空间。
但是,如果派生类的第一个非静态成员的类型和基类相同,由于 C++ 标准要求相同类型的不同对象的地址必须不同,编译器就会为基类分派一个字节的地址空间。
PODType
std::is_trivial && std::is_standard_layout == true
C++ 中,“常量指针”(指向常量的指针)和“指针常量”(常量指针)
常量指针(指向常量的指针)
指向常量的指针意味着指针所指向的值是常量,不能通过该指针修改值。const int* ptr;
#include <iostream>
int main() {
int value = 10;
const int* ptr = &value; // 指向常量的指针
std::cout << "Value: " << *ptr << std::endl;
// *ptr = 20; // 错误:不能通过指向常量的指针修改值
value = 20; // 直接修改值是允许的
std::cout << "Value: " << *ptr << std::endl;
return 0;
}
在这个示例中,ptr 是一个指向常量的指针,你不能通过 *ptr 来修改 value 的值。
指针常量(常量指针)
常量指针意味着指针本身是常量,一旦指向了某个地址,不能再指向其他地址。语法:int* const ptr;
#include <iostream>
int main() {
int value1 = 10;
int value2 = 20;
int* const ptr = &value1; // 指针常量
std::cout << "Value1: " << *ptr << std::endl;
*ptr = 30; // 允许通过指针常量修改值
std::cout << "Modified Value1: " << *ptr << std::endl;
// ptr = &value2; // 错误:不能修改指针常量的地址
return 0;
}
在这个示例中,ptr 是一个常量指针,它指向 value1,你不能将 ptr 指向其他地址,但是可以通过 *ptr 修改 value1 的值。