You are on page 1of 18

content http://10.214.164.125/yy/cplusplus/content.

html

Modern C++
exception

相较于C用返回值提示程序运行状态,C++引入了异常处理机制来处理程序出错的情况。

C语言程序出错处理风格
通过显式地处理每步错误,并返回错误码给上层函数,以通知执行错误。递归如上操作,直到
找到能处理错误的函数。

1 of 18 18/12/2020, 3:30 pm
content http://10.214.164.125/yy/cplusplus/content.html

int readFile(const char* filepath, char** buffer, int** offsets, int* lineCount)
FILE* fp = fopen(filepath, "r");
if (!fp) { // <---------------------------------------------- check error
return -1; // cannot open file
}

// get statistics of the file


int byteCount = 0; *lineCount = 0;
while (1) {
int ret = fgetc(fp);
if (ret == EOF) break; // <------------------------------ check error
if ((char)ret == '\n') ++lineCount;
++byteCount;
}
byteCount += lineCount; // '\0' requires extra bytes
if (feof(fp)) { // <----------------------------------------- check error
fclose(fp); // <---------------------------------------- release resources
return -100; // file operation error
}
rewind(fp);

// allocate memory
*buffer = (char*)malloc(byteCount * sizeof(char));
*offsets = (int*)malloc(lineCount * sizeof(int));
*lines = lineCount;
if (*buffer == NULL || *offsets == NULL) {// <--------------- check error
fclose(fp); // <---------------------------------------- release resources
return -10000; // out of memory
}

// record data
char* p = *buffer;
offsets[0] = 0;
for (int i = 0; i < lineCount; ++i) {
if (fgets(p, bytesCount, fp) == NULL) {// <-------------- check error
*lines = i - 1; // record valid lines
fclose(fp); // <------------------------------------- release resources
return -100; // file operation error
}
if (i > 0)
offsets[i] = offsets[i - 1] + strlen(p);
p += lineLengths[i];
}

fclose(fp); // <--------------------------------------------- release resources


return 1; // success
}

C++程序出错处理风格
通过异常抛出错误, 只处理当前函数能处理的错误。

2 of 18 18/12/2020, 3:30 pm
content http://10.214.164.125/yy/cplusplus/content.html

/* @file ./codes/exception/example1.cpp */
std::vector<std::string> readFile(const std::string filepath) {
std::vector<std::string> lines;
std::ifstream fs(filepath, std::ios::in);

try {
fs.exceptions(std::ifstream::failbit | std::ifstream::badbit);

std::string line;
while (getline(fs, line)) {
lines.push_back(line);
}
return lines;
} catch (std::ifstream::failure& e) {
if (!fs.eof()) { // getline will cause failbit at the end of the file
throw std::runtime_error("read " + filepath + " error: " + e.what())
}
} catch (std::bad_alloc& e) {
throw std::runtime_error(std::string("out of memory: ") + e.what());
}

return lines;
}

异常的使用
C++标准库为我们提供了如下的异常,定义在头文件stdexcept中。
这些异常也被用在标准库的其他组件中,如vector, iostream等

我们可以(通过继承标准库中的异常)自定义新的异常,使程序更好地处理不同的错误

3 of 18 18/12/2020, 3:30 pm
content http://10.214.164.125/yy/cplusplus/content.html

/* @file ./codes/exception/example2.cpp */
class OutOfStock: public std::runtime_error {
public:
explicit OutOfStock(const std::string &s) : std::runtime_error(s) {};
};

class IsbnMismatch: public std::logic_error {


public:
IsbnMismatch(const std::string& s, const std::string& lhs, const std::string&
: std::logic_error(s), left(lhs), right(rhs) {}
const std::string left, right;
};

noexcept关键字
与异常相关的另一语法是noexcept,用以说明函数不会抛出异常,提高程序可解释度,并帮助编
译器执行对无异常代码的特殊优化。

void f(void) noexcept;


class A {
void f(void) const noexcept = 0;
void g(void) const noexcept override;
};

noexcept修饰的函数一致性问题

void f(int) noexcept; // will not throw except


void g(int); // might throw except
void (*pf1)(int) noexcept = f; // ok
void (*pf2)(int) noexcept = g; // error
void (*pg1)(int) = f; // ok
void (*pg2)(int) = g; // ok

noexcept修饰的类成员函数一致性问题

class Base {
public:
virtual void f() noexcept; // will not throw except
virtual void g(); // might throw except
};

class Derived : public Base {


public:
void f() override; // error
void f() noexcept override; // ok
void g() override; // ok
void g() noexcept override; // ok
};

值得指出的是编译器并不会在编译时检查noexcept说明,即如果noexcept中抛出异常,是能通
过编译的。

4 of 18 18/12/2020, 3:30 pm
content http://10.214.164.125/yy/cplusplus/content.html

但运行时,如果抛出异常,程序则会调用terminate以确保遵守不在运行时抛出异常的承诺。
因此noexcept可以被用在如下情况:1.函数不会抛出异常,2.我们确实无法处理异常。

/* @file ./codes/exception/example3.cpp */
void f(void) noexcept {
std::cout << "void f() noexcept" << std::endl;
throw std::runtime_error("throw an except"); // program terminates
}

lambda表达式

lambda表达式是C++11最好用的语法糖之一,匿名函数意味着无需在全局定义函数,只要就地展开
函数体即可。

语法标准

[ capture ] ( params ) opt -> return_type { /*function body*/ };

// [ capture ] 捕获列表,可以是如下之一
// [], 空
// [=], lambda所在范围内的所有可见变量,包括lambda表达式所在类的this指针,以值传递
// [&], lambda所在范围内的所有可见变量,包括lambda表达式所在类的this指针,以引用
// [this] lambda所在类的所有成员变量与函数
// [a] 将变量a按值传递,且默认是const(opt被省略时),即lambda表达式内不可修改a的值
// [&a] 将变量a以引用传递
// [a, &b] 将变量a以引用传递, b以引用传递
// [=, &a, &b] 除a, b以引用传递外,其余变量均按值传递
// [&, a, b] 除a, b以值传递传递外,其余变量均按引用传递

// ( params ) 函数参数列表,当没有参数时,可以省略()

// opt 为mutable或exception
// mutable 用以标识值传递的变量是可修改的
// exception 用以表示lambda表达式可能抛出异常

// -> return_type 返回值类型,当函数体中只有一处return或返回值为void时,可以省略

// { /*function body*/ } 函数体,不能省略

/* @file ./codes/lambda/example1 */
auto add = [](int i, int j){ return i + j; };
add(1, 2); // return 1 + 2

class A {
public:
int call() { return [this] { return add(m_i, m_j); }(); }
private:
int m_i = 1, m_j = 2;
int add(int i, int j) { return i + j; }
};

5 of 18 18/12/2020, 3:30 pm
content http://10.214.164.125/yy/cplusplus/content.html

lambda表达式调用标准库函数
C++标准库集成了lambda表达式例子

std::vector<int> v(1000);
std::for_each(v.begin(), v.end(), [](int& x) {
x = 0;
});

lambda表达式与外部库函数结合使用
由于越来越多的库,如tbb和cuda,开始支持Modern C++的语言特性,使用lambda表达式能更
简单地调用库函数

// calculate `unique` morton code for all nodes


// nvcc compile flag --extended-lambda must be set
thrust::device_vector<uint64_t> mortonCodes(objectCount);

thrust::device_vector<uint32_t> indices(objectCount);
thrust::copy(thrust::make_counting_iterator<uint32_t>(0),
thrust::make_counting_iterator<uint32_t>(objectCount), indices.begin());

thrust::transform(m_objects.begin(), m_objects.end(), indices.begin(), mortonCodes


[box_whole]__device__(const XyzToRgb<float>& object, const uint32_t& index)
Vec3<float> vmax = box_whole.getMax();
Vec3<float> vmin = box_whole.getMin();
Vec3<float> p = object.xyz();
p[0] = (p[0] - vmin[0]) / (vmax[0] - vmin[0]);
p[1] = (p[1] - vmin[1]) / (vmax[1] - vmin[1]);
p[2] = (p[2] - vmin[2]) / (vmax[2] - vmin[2]);

return (static_cast<uint64_t>(calcMortonCode(p[0], p[1], p[2])) << 32) + index


}
);

左值与右值

左值与右值是C++11最重要的概念。右值的引入是为了解决对象多次构造问题。
通过引入右值,编译器就能从右值中窃取资源,而不是重复拷贝。

6 of 18 18/12/2020, 3:30 pm
content http://10.214.164.125/yy/cplusplus/content.html

std::string s = std::string("a really really long string");

// C++98 works like this


// 1. construct the temporary object std::string("a really really long string")
// 2. call the `COPY` constructor of string to deep copy the content of the temporary obje
// 3. the full expression ends, temporary object is destroyed.
// note: the content "a really really long string" is allocated in memory twice

// C++11 works like this


// 1. construct the temporary object std::string("a really really long string")
// 2. call the `MOVE` constructor of string to `STEAL` the content of the temporary object
// 3. the full expression ends, temporary object is destroyed.
// note 1: the content "a really really long string" is allocated in memory only once
// note 2: after step 2, the temporary object is invalid

左值与右值的概念
在C++中所有表达式都被如下的分类系统归类。

expression
/ \
gvalue rvalue
/ \ / \
lvalue xvalue prvalue

lvalue即左值,表示内存中实际存在的一块持久存储的区域,在表达式结束后依然占据有效内
存。
rvalue即右值,表示表达式结束后就不存在的临时对象。
evalue即消亡值,也就是即将被销毁、却能够被移动的值。
prvalue即纯右值, 如纯粹的字面量,或字面量表达式,Lambda 表达式等。
右值引用
C++ 定义&&符号为右值引用(不包括模板的情况),右值引用可以以右值为初值构造,延续右值的
生命周期。

int&& i = 1;
int&& j = i++;
double&& d = 1.0 + 2.0;

可以利用std::move进行左值到右值的转换。

std::vector<int> foo = {1, 2, 3, 4};


std::vector<int>&& v = std::move(foo);

以下给出std::move源码

7 of 18 18/12/2020, 3:30 pm
content http://10.214.164.125/yy/cplusplus/content.html

/* @file vcruntime.h */
#define _NODISCARD [[nodiscard]] // attribute

/* @file xtr1common */
template <class _Ty> // generalized
struct remove_reference { using type = _Ty; }; // simplified version

template <class _Ty> // speciallized


struct remove_reference<_Ty&> { using type = _Ty; }; // simplified version

template <class _Ty> // specialized


struct remove_reference<_Ty&&> { using type = _Ty; }; // simplified version

template <class _Ty> // alias declaration


using remove_reference_t = typename remove_reference<_Ty>::type;

/* @file type_traits */
template <class _Ty>
_NODISCARD constexpr remove_reference_t<_Ty>&& move(_Ty&& _Arg) noexcept {
return static_cast<remove_reference_t<_Ty>&&>(_Arg);
}

移动语义
我们可以在类中定义移动构造函数,该函数接受一个右值作为参数,并将资源传递给要构造的
新对象。

/*@file ./codes/lvalue and rvalue/example1.cpp */


class A {
public:
A(): buffer(new char[4096]) { }

A(const A& a): buffer(new char[4096]) {


buffer = new char[4096];
for (std::size_t i = 0; i < 4096; ++i) {
buffer[i] = a.buffer[i];
}
}

A(A&& a) noexcept { // guarentee no exception throws


buffer = a.buffer;
a.buffer = nullptr; // invalidate object a
}

~A() { if (buffer) delete[] buffer; }


private:
char* buffer = nullptr;
};

多线程

C++正式引入了多线程编程模型,充分利用现代处理器多核性能。

8 of 18 18/12/2020, 3:30 pm
content http://10.214.164.125/yy/cplusplus/content.html

std::thread

std::vector<std::thread> threads;

for (int i = 0; i < 100; ++i) {


std::thread t([i] {
std::ostringstream oss;
oss << std::this_thread::get_id();
std::string tid = oss.str();
printf("tid %s: %d\n", tid.c_str(), i);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
});

threads.push_back(std::move(t));
}

for (auto& t : threads) {


t.join();
}

std::mutex 与 std::condition_variable
std::mutex就是我们在操作系统中使用到的互斥锁。

std::mutex mtx;
mtx.lock();
// critical section
mtx.unlock();

std::condition_variable则是比较新的内容,熟悉pthread库的用户应该对此并不陌生。
std::condition_variable与std::mutex配合使用,下面演示使用上述两者创建多线程队列。

9 of 18 18/12/2020, 3:30 pm
content http://10.214.164.125/yy/cplusplus/content.html

/* @file ./codes/multithreading/example2.cpp */
template <typename T>
class ConcurrentQueue {
public:
void push(T value) {
std::lock_guard<std::mutex> lockGuard(m_mtx);
m_dataQueue.push(std::move(value));
m_cond.notify_one();
}

bool tryPop(T& value) {


std::lock_guard<std::mutex> lockGuard(m_mtx);
if (m_dataQueue.empty()) {
return false;
}

value = std::move(m_dataQueue.front());
m_dataQueue.pop();
return true;
}
private:
std::mutex m_mtx;
std::condition_variable m_cond;
std::queue<T> m_dataQueue;
};

std::atomic
std::atomic为C++11提供的原子操作库,主要包含std::atomic_flag与std::atomic<T>的模板对
象。
std::atomic_flag使用非常简单,仅有初始化、test_and_set与clear操作,下面我们使用
std::atomic_flag开发一个自旋锁。

/* @file ./codes/multithreading/example3.cpp */
class SpinLock {
public:
void lock() { while (m_flag.test_and_set()); }
void unlock() { m_flag.clear(); }
private:
std::atomic_flag m_flag = ATOMIC_FLAG_INIT;
};

std::atomic<T>提供了我们更多地操作原子变量的机会。

10 of 18 18/12/2020, 3:30 pm
content http://10.214.164.125/yy/cplusplus/content.html

#define [[nodiscard]] _NODISCARD


using std::atomic;
using std::memory_order;

template<typename T> // return the value of atomic variable


_NODISCARD T load(const memory_order order) const volatile noexcept;

template<typename T> // set the value of atomic variable


_NODISCARD void store(const T value, const memory_order order) volatile noexcept

template<typename T> // exchange the value of atomic variable and return the old value
_NODISCARD T exchange(const T value, const memory_order order) volatile noexcept

template<typename T> // compare and swap


_NODISCARD bool compare_exchange_weak(
T& expected, const T desired, const memory_order order) volatile noexcept;

template<typename T> // compare and swap


_NODISCARD bool compare_exchange_strong(
T& expected, const T desired, const memory_order order) volatile noexcept;

在深入了解atomic之前,我们必须引入内存序的概念。
内存序定义了在单线程中的指令重排约束,并定义了多线程间的同步关系。

// classification store load read-modify-write memory order model


std::memory_order_seq_cst // Y Y Y sequential consistency
std::memory_order_acquire // Y Y acquire release
std::memory_order_release // Y Y acquire release
std::memory_order_acq_rel // Y acquire release
std::memory_order_consume // Y Y acquire release
std::memory_order_relaxed // Y Y Y relaxed ordering

上面的自旋锁版本中,,可用acquire release语义做进一步优化

/* @file ./codes/multithreading/example3.cpp */
class SpinLock {
public:
void lock() { while (m_flag.test_and_set(std::memory_order_acquire)); }
void unlock() { m_flag.clear(std::memory_order_release); }
private:
std::atomic_flag m_flag = ATOMIC_FLAG_INIT;
};

注意误用memory_order会造成很大问题,在特定的平台上可能是正确的,但不能保证所有平台
都是正确的。

11 of 18 18/12/2020, 3:30 pm
content http://10.214.164.125/yy/cplusplus/content.html

std::atomic<bool> x(false), y(false);


std::atomic<int> z(0);

void write_x() { x.store(true,std::memory_order_release); } // thread1

void write_y() { y.store(true,std::memory_order_release); } // thread2

void read_x_then_y() { // thread3


while(!x.load(std::memory_order_acquire));
if(y.load(std::memory_order_acquire))
++z;
}

void read_y_then_x() { // thread4


while(!y.load(std::memory_order_acquire));
if(x.load(std::memory_order_acquire))
++z;
}

上述4个线程执行完成后,z的值理论上可能依然为0,这是因为同步关系仅存在两线程之间。

智能指针

内存管理一直是C++令人比较头疼的问题,智能指针则是C++内存管理最佳实践,其本质上是个资
源所有权问题。
如果手动管理内存,在函数的多个出口点,都需要显式地调用释放资源函数来清理内存,这是十分
反人类的。
另外在多线程框架下,不同线程需要共享数据,智能指针确保持有智能指针的线程的数据一定是有
效的。

std::unique_ptr
unique说明其管理的对象是独占的,因而禁止其他智能指针与其共享同一个对象。

12 of 18 18/12/2020, 3:30 pm
content http://10.214.164.125/yy/cplusplus/content.html

std::unique_ptr基本使用

/* ./codes/smart pointer/example1.cpp */
std::unique_ptr<int> p1(new int(0));
std::unique_ptr<int> p2 = p1; // error, copy constructor is deleted
std::unique_ptr<int> p3 = std::move(p1); // ok, ownership is given to p3

p1.reset(new int); // p1 points to a new memory address


*p1 = 100; // *p1 = 100, *p3 = 0

std::unique_ptr做资源管理

/* ./codes/smart pointer/example2.cpp */
void printFile(const char* filepath) {
FILE* fp = fopen(__FILE__, "r");
if (fp == NULL) {
std::cerr << "cannot open file" << std::endl;
return;
}

// define a guard to close file


std::unique_ptr<FILE, void (*)(FILE* fp)> resourceGuard(fp, [](FILE* fp) {
std::cout << "resourceGuard: close file" << std::endl;
fclose(fp);
});

// some operations on file


while (!feof(fp)) {
char c = fgetc(fp);
std::cout << c;
}
}

**std::shared_ptr
std::shared_ptr通过引用计数的方式,记录多少个shared_ptr指向同一对象,当引用计数为0
时,自动删除对象。

/* ./codes/smart pointer/example3.cpp */
void foo(std::shared_ptr<int> i) {
std::cout << "foo: " << i.use_count() << std::endl; // foo: 2
(*i)++;
}

// ...
std::shared_ptr<int> p1 = std::make_shared<int>(10); // value: 10
std::cout << "main: " << p.use_count() << std::endl; // main: 1
foo(p1); // after the expression, the content of p1 would be 11;

std::weak_ptr
为避免循环引用,我们需要引入std::weak_ptr

13 of 18 18/12/2020, 3:30 pm
content http://10.214.164.125/yy/cplusplus/content.html

struct A {
std::shared_ptr<B> pointer; // must change one of pointer to std::weak_ptr
};

struct B {
std::shared_ptr<A> pointer; // must change one of pointer to std::weak_ptr
};

//...
auto a = std::make_shared<A>();
auto b = std::make_shared<B>();
a->pointer = b;
b->pointer = a; // memory leak

杂项

using关键字
using 定义类型别名

typedef float real_t; // C++98


using real_t = float; // C++11

typedef int(*function_t)(int, int); // C++98


using function_t = int (*)(int, int); // C++11

template <typename T> // C++98


struct str_map_t {
typedef std::map<std::string, T> type;
}; // one can use `str_map_t<int>::type strMap;` to create the object named strMap

template <typename T> // C++11


using str_map_t = std::map<std::string, T>;

using random_t = std::default_random_engine; // simplify name


using clock_t = std::chrono::high_resolution_clock; // simplify name
using directory_t = std::filesystem::directory_entry; // simplify name

using 引入命名空间与外部类型

using std::vector;
using namespace std;

using 重载父类函数
在子类中使用using声明引入基类成员名称

14 of 18 18/12/2020, 3:30 pm
content http://10.214.164.125/yy/cplusplus/content.html

class Base {
public:
std::size_t size() const { return n; } // will be private in derived class
protected:
std::size_t n; // will be private in derived class
};

class Derived: private Base { // private inheritance


public:
using Base::size; // size can be used as a public member function of Derived
protected:
using Base::n; // n can be visited in the derived class of Derived
};

using 定义继承构造
传统C++子类构造函数如果需要继承,则需要一一传递参数,这将导致效率低下。C++11
引入继承构造函数的概念。

/* @file ./codes/miscellaneous/example1.cpp */
class Base {
public:
Base() { value1 = 1; }
Base(int value) : Base() { // delegating constructor
value2 = value;
}
private:
int value1, value2;
};

class Derived : public Base {


public:
using Base::Base; // inheriting constructor
};

枚举类
C++11引入了新的限定作用域的枚举类,用以解决之前重名问题。
基本语法

// c++98 style
enum Color {RED, BLUE, GREEN};
enum DressColor {BLUE, ORANGE, GREEN}; // `BLUE` conflicts with a previous declaration
Color color = RED; // ok
int value = RED; // ok

// c++11 style
class enum Color {RED, BLUE, GREEN};
class enum DressColor {BLUE, ORANGE, GREEN}; // ok
Color color = Color::RED; //ok
Color color = RED; // error
int value = Color::RED; // error, even if the RED is int by default, no implicit conver

15 of 18 18/12/2020, 3:30 pm
content http://10.214.164.125/yy/cplusplus/content.html

定义枚举类的类型
枚举类型变量的默认值为int,但我们可以为枚举类定义不同的类型,以满足我们的实际需
求。

enum class Level : char {


EASY = 'a', NORMAL = 'b', HARD = 'c'
};

auto关键字
auto优点
使用auto关键字,能大大节约我们的脑力与打字量。编译器通过推导自动获取auto对应的
类型。

std::vector<double> v;

std::vector<double>::iterator it1 = v.begin(); // c++98


auto it2 = v.begin(); // c++11

std::vector<double>::size_type size1 = v.size(); // c++98


auto size2 = v.size(); // c++11

std::unordered_multimap<int, int> umap;

std::pair<std::unordered_multimap<int, int>::iterator,
std::unordered_multimap<int, int>::iterator> range1 = umap.equal_range(key);
auto range2 = umap.equal_range(key); // c++11

auto lambdaFunction = [](int i, int j) { return i > j; }; // c++11

auto类型推导

auto n = 10; // int


auto f = 12.8; // double
auto p = &n; // int*
auto url = "syby119@126.com"; // const char*

int x = 0;
auto *p1 = &x; // auto->int
auto p2 = &x; // auto->int*
auto &r1 = x // auto->int
auto r2 = r1; // auto->int, & is removed

const auto n = x; // auto->int


auto f = n; // auto->int
const auto &r1 = x; // auto->int
auto &r2 = r1; // auto const int

循环的几种写法
C++11引入了多种循环遍历方法,使得书写循环变得更加简单。

16 of 18 18/12/2020, 3:30 pm
content http://10.214.164.125/yy/cplusplus/content.html

/* ./codes/miscellaneous/example3.cpp */
std::vector<double> v(2'0000'0000);

for (std::vector<double>::size_type i = 0; i < v.size(); ++i) { /*...*/ }


for (auto it = v.begin(); it != v.end(); ++it) { /*...*/ }
for (auto& e : v) { /*...*/ }
std::for_each (v.begin(), v.end(), [](double& e){ /*...*/});
std::for_each (std::execution::par, v.begin(), v.end(), [](double& x) { /*...*/ }

列表初始化
为进一步统一C++中对象的初始化语法,C++11引入了列表初始化
语法

T a {value1, value2, [value3, ...]};


T a = {value1, value2, [value3, ...]}; // both ok

int i{1};
double d{1.0};
int a[] = { 1, 2, 3 };
int *p = new int[3]{1, 2, 3};
std::vector<int> v = {1, 2, 3};

std::vector<int> f() { return {1, 2, 3}; }

class A {
public:
A(int i, int j): v{2} {}
private:
int v;
};

A a{1, 2};

列表初始化解决窄化问题

int a = { 1.1 }; // error


float fb = { 1e40 } // error

const int x = 1024, y = 1;


char d = { x }; // error
char f = { y }; // ok, because of const

列表初始化解决most vexing parse

std::string str1(std::string()); // declare a function named str1, with a param std::st


std::string str2((std::string())); // works
std::string str3{std::string()}; // works

内存对齐
不同编译器的内存方式不同,这给程序移植造成了一定的困难。

17 of 18 18/12/2020, 3:30 pm
content http://10.214.164.125/yy/cplusplus/content.html

struct A { // MSVC32 gcc32


// start size padding | start size padding
char a; // 0x00 1 3 | 0x00 1 3
int b; // 0x04 4 0 | 0x04 4 0
short c; // 0x08 2 6 | 0x08 2 2
long long d; // 0x10 8 0 | 0x0c 8 0
char e; // 0x18 1 7 | 0x14 1 3
}; // total 32 16 16 | 24 16 8

通过alignas关键字,我们可以指定内存对齐的大小,既可以对普通变量的生效,也可以对结构体
生效。
注意alignas(n)中的n只能是2的次方,如1, 2, 4, 8, 16, 32, ...。且对齐结果只大不小。

/* @file ./codes/miscellaneous/example5.cpp */
struct A { // size padding
char c; // 1 3
int i; // 4 0
double d; // 8 0
}; // total 16

struct alignas(32) B { // size padding


char c; // 1 3
int i; // 4 0
double d; // 8 16
}; // total 32

struct C { // size padding


char c; // 1 7
alignas(8) int i; // 4 4
double d;; // 8 0
}; // total 24

另外,在手动分配内存时,我们需要对齐的内存,以支持一些SIMD指令,如Intel的AVX指令集

template <std::size_t Len, std::size_t Align>


struct aligned_storage; // c++11, after allocate memory, use placement new to create an

void* aligned_alloc(size_t alignment, size_t size ); // c++17, not supported by VS2019


void* operator new(std::size_t count, std::align_val_t al); // c++17, not supported by

18 of 18 18/12/2020, 3:30 pm

You might also like