Skip to content

学习高阶C++

原子性:指事务的不可分割性,一个事务的所有操作要么不间断地全部被执行,要么一个也没有执行。 资源获取即初始化 - RAII(Resource Acquisition Is Initialization)

1、指向结构体的指针必须初始化

#include<stdio.h>
void main()
{
	struct abc{
	int a;};
	struct abc *p;
	p->a=1;
	printf("%d",p->a);
}

这个编译没有问题,但是运行是段错误,请问为什么呢 因为你定义了一个结构体指针p,用来指向此类结构体,但是你却没有给他赋值,此时p的值为NULL,你并没有在内存中为p分配任何空间,所以p->a=1这句就会出段错误。

修改方法1:可以给p分配一段内存空间,并使其指向此空间: p=(struct abc *)malloc(sizeof(struct abc)); p->a = 1; 方法2:可以让p指向一个已存在的内存空间: struct abc temp; p=&temp; p->a = 1;

2、高级宏展开

//此宏展开后,类似于printf("%d""%d", 1, 2);  
#define TRACE_CMH_2(fmt, ...) \  
    printf("%s(%d)-<%s>: "##fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)

3、warning: function declaration isn’t a prototype(函数声明不是原型)的解决办法

原因是无参函数报的警告,只需要添加void即可。

static int hello_init(void)
{
    printk(KERN_EMERG   "hello world!\n");
    return 0;
}

4、细节决定成败

我的小伙伴还以为是编译器长时间不用凉了么

int j = 0;
for (j = 0; j < 10; j++);
{
	printf("j = %d\n", j);
}

输入结果是j = 10只有一行
如果是1的,肯定就是一行,这就会很迷惑。因此要养成良好的代码习惯。
我见过for循环末尾,函数末尾添加分号的,这种情况虽然没有影响,但是个人觉得还是不要有才好。

5、c++标准发展史

1998年,C++的ANSI/IS0标准被投入使用。 C++ 03 C++ 11 C++ 14 C++ 17 C++ 20

[root@ubuntu0006:~/cmake] #g++ a.cpp -std=c++17
[root@ubuntu0006:~/cmake] #g++ a.cpp -std=c++19
g++: error: unrecognized command line option ‘-std=c++19’
[root@ubuntu0006:~/cmake] #g++ a.cpp -std=c++20
g++: error: unrecognized command line option ‘-std=c++20’
[root@ubuntu0006:~/cmake] #g++ --version
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

虽然编译的时候能识别-std=c++17,但是由于编译器版本过旧,实际上是不支持的。编译器版本过旧:确保你的编译器版本足够新,以支持 C++17 中的结构化绑定。例如,GCC 需要至少是 7.1 版本,Clang 需要至少是 4.0 版本。

6、c++11新特性

https://zhuanlan.zhihu.com/p/650986900https://m.baidu.com/sf?pd=topone_multi&top={"sfhs"%3A1}&atn=index&word=c%2B%2B11新特性&lid=16524987177891995353&key=ODjAz5JIKOstxE1s4UBHiW1Rd8Dz4F%2F8%2FVCvqUmSw10p7xL%2FVg1lVWSpvCcpjntjo0LSAunaVLhKqIifyl9fhmAxtz94ezwv9I7NuBdI%2Fos%3D&type=bpage

c++11关于继承新增了两个关键字,final用于修饰一个类,表示禁止该类进一步派生和虚函数的进一步重载,override用于修饰派生类中的成员函数,标明该函数重写了基类函数,如果一个函数声明了override但父类却没有这个虚函数,编译报错,使用override关键字可以避免开发者在重写基类函数时无意产生的错误。

7、c++14新特性

c++14并没有太大的改动,就连官方说明中也指出,c++14相对于c++11来说是一个比较小的改动,但是在很大程度上完善了c++11,所以可以说c++14就是在c++11标准上的查漏补缺。

C++14 is a minor but important upgrade over C++11, and largely “completes C++11.”

8、c++17新特性

9、c++20新特性

10、左值表达式和右值表达式

左值:有名字、能取地址、可以被赋值。 右值:没有名字、不能取地址、不能被赋值,又可进一步分为纯右值和将亡值。 在 C++11 及以后的版本中,右值又细分为纯右值(prvalue)和将亡值(xvalue)。将亡值通常是通过 std::move() 或者返回对象的右值引用(T&&)得到的,它表示资源可以被转移的对象。

11、右值引用(Rvalue References)

右值引用是 C++11 引入的一种新引用类型,用于绑定到右值(临时对象)。它的主要目的是实现移动语义和完美转发。

int&& rref = 42;       // 绑定到整数字面量(右值)
std::string&& sref = std::string("temp");  // 绑定到临时字符串

只能绑定到右值:不能直接绑定到左值(变量)。 延长生命周期:通过右值引用绑定临时对象后,其生命周期会延长到引用的作用域结束。 移动语义:右值引用是实现移动构造函数和移动赋值运算符的基础。

12、什么是完美转发

完美转发指的是在函数模板中,将参数以原始的值类别(左值或右值)传递给其他函数,避免不必要的拷贝或移动操作。 std::forward 是 C++ 中用于实现完美转发的关键工具,它能在函数模板中保持参数的原始值类别(左值或右值)。

关键场景:

  • 转发构造函数参数(如智能指针、工厂函数)。
  • 实现通用的包装器或代理函数。

正确使用 std::forward 的三个核心条件

  • 使用转发引用(Universal Reference) 函数模板的参数必须是 T&& 形式,其中 T 是通过模板类型推导得出的。
  • 使用 std::forward<T>(arg) 转发时必须显式指定模板参数 T,即 std::forward<T>(arg),其中 T 是函数模板的类型参数。
  • 仅在需要保持值类别时使用 不要在不需要转发的地方使用 std::forward(如普通函数、已知值类别的场景)。

13、CPU 的 CAS 操作

CAS(Compare-And-Swap)是一种硬件级别的原子操作,用于实现无锁(lock-free)算法。它允许程序在不使用锁的情况下原子地更新共享数据,从而显著提高多线程程序的性能。

基本概念

  • 核心思想:“先比较内存中的值是否等于期望值,如果相等则更新为新值,否则返回失败”。
  • 原子性保证:整个操作由 CPU 硬件直接支持,不可被中断。
  • 常见实现:C++:std::atomic<T>::compare_exchange_weak/strong

14、自旋锁(Spinlock)

自旋锁(Spinlock)是一种用于多线程同步的锁机制,它通过让线程持续循环(自旋)检查锁的状态,而不是进入睡眠状态来避免锁竞争。自旋锁适用于锁持有时间短、线程不希望在获取锁失败时被挂起的场景。

15、SFINAE技术

SFINAE(Substitution Failure Is Not An Error)是 C++ 模板编程中的一种重要技术,允许编译器在模板实例化时自动忽略不匹配的模板,而不是产生编译错误。这使得我们可以根据不同条件选择不同的实现,是现代 C++ 元编程的基石之一。

核心思想 当模板参数替换导致无效的类型或表达式时,编译器不会报错,而是选择其他更匹配的模板(如果存在)。

16、仅了解

std::enable_if、std::true_type 和 std::false_type 是 C++ 模板元编程中的核心工具,用于在编译时进行条件判断和类型选择。它们通常与 SFINAE(Substitution Failure Is Not An Error)技术结合使用,实现模板的条件编译。

17、语法糖

在编程语言中,语法糖(Syntactic Sugar) 是指那些为了让代码更易读、更简洁而设计的语法特性。它们不会增加语言的功能,但可以让代码书写更加优雅、直观。C++ 中有许多语法糖,理解它们有助于写出更高效的代码。

  • 范围 for 循环(C++11)
  • 自动类型推导(C++11)
  • Lambda 表达式(C++11)
  • 折叠表达式(C++17) 是典型的语法糖。它解决了可变参数模板展开的繁琐问题
  • 初始化列表(C++11)
  • 智能指针(C++11)
  • 结构化绑定(C++17)

语法糖的优缺点 优点 代码简洁:减少样板代码,提高可读性 减少错误:自动化常见操作(如内存管理) 表达力强:更直观地表达编程意图

缺点 学习成本:新语法需要时间理解 编译复杂性:某些语法糖可能增加编译时间 隐藏细节:过度使用可能导致开发者忽略底层原理

18、副作用

在编程中,副作用(Side Effect) 指的是函数或表达式在计算结果之外,对程序的外部状态产生的可观察的影响。这些影响可能包括修改全局变量、更新文件系统、打印输出、发送网络请求等。副作用是程序与外部环境交互的重要方式,但过度使用可能导致代码难以理解和调试。

纯函数(Pure Function) 是指没有副作用的函数:

  • 相同输入始终返回相同输出
  • 不修改外部状态
  • 不依赖外部状态

19、仿函数

在 C++ 中,仿函数(Functor)也被称为函数对象(Function Object),是一种能够像函数一样被调用的对象。它的本质是一个类或结构体,通过重载operator()(函数调用运算符)来实现函数调用的语法。这种设计模式使得对象可以表现得如同普通函数一样,并且可以携带额外的状态。

核心特点

  • 可调用性:通过重载operator(),仿函数可以像普通函数一样被调用。
  • 状态存储:仿函数可以包含成员变量,从而在多次调用之间保持状态。
  • 类型安全:与普通函数指针相比,仿函数是类型安全的,因为编译器可以检查其参数和返回值类型。
  • 内联优化:由于仿函数是编译时确定的,编译器更容易对其进行内联优化,提高性能。

This website is released under the MIT License.