读书笔记<高速上手C11 14 17>

1.编译原理

Gcc g++ 编译过程

2.语言可用性的强化

2.1常量

  1. nullptr

    nullptr 的出现主要是替换NULL,在C语言中NULL表示的是(void*(0)),而在C++ 中表示0,
    C++ 不允许直接将void* 隐式转换到其他类型

// #define NULL 0
// 导致C++ 重载特性发生混乱
void test(char*);
void test(int);
test(NULL);

因此C++ 引入nullptr 区分0 和 空指针, nullptr的类型是nullptr_t 可以隐式转换成任意类型的指针

#include <iostream>
#include <type_traits>

using namespace std;

void test(int i){
	cout <<__FUNCTION__ <<endl;	
}
void test(char *){
	cout <<__FUNCTION__ <<endl;
}
int main(){
	
	if (is_same<decltype(NULL),decltype(0)>::value)
		cout << "NULL == 0" <<endl;
	if (is_same<decltype(NULL),decltype(void*(0))>::value)
		cout << "NULL == void*(0)" <<endl;
	if (is_same<decltype(NULL),decltype(nullptr_t)>::value)
		cout << "NULL == nullptr_t" <<endl;	
	
	test(0);
	test(nullptr);
	// test(NULL);
	return 0; 
}
  1. constexpr

    const 作为常量, 而constexpr 是修饰常量表达式,即在编译前可以确定表达式的值;

constexpr int getNum(){
	return 10;
}
const int num = 10;
constexpr int sum = num * 2;
int g_list[sum]={0};
const len = num + 2;
char arr_4 [len]; // 数组的长度必须是一个常量表达式

2.2 变量及其初始化

  1. if/switch 变量声明强化
    if 和 switch 中无法声明一个变量
// C++ 17 中是允许的
if ( const auto itr = std::find( vec.begin (), vec.end() , 3); 3 itr != vec . end () ) {
 * itr = 4;
}
  1. 初始化列表
    std::initializer_list
    内置类型可以直接使用;
class MagicFoo {
public:
   vector<int> m_vec;
   // 初始化列表构造函数
   MagicFoo(initializer_list<int> list) {
       for (auto it = list.begin(); it != list.end(); ++it) {
           m_vec.emplace_back(*it);
       }
   }
};
int main()
{
   MagicFoo magicFoo = { 1,2,3,4,5 };
   MagicFoo magicFoo2{ 1,2,3,4,5 };
   int data{10};
   return 0;
}
  1. 结构化绑定
    std::tuple
// C++17的支持
#include <tuple>

std::tuple<int, double, std::string> f() {
    return std::make_tuple(1, 2.0, "test");
}
int main()
{
    auto [x, y, z] = f();
    cout << x << "-" << y << "-" << z << endl;

    return 0;
}

2.3 类型推导

  1. auto
    auto 作为静态类型推导,不可以作为函数的参数
// 在函数的para 有默认值时可以使用
int test(auto data = 0) {
    return data;
}
test(10); 

// auto 还不能用于推导数组类型
 auto arr = new auto (10);
 auto data[20] = arr;
  1. decltype 推导表达式的类型
auto x = 10;
decltype(x) y=20;

  1. 尾返回类型推导
// 
template < typename R , typename T , typename U >
R add ( T x , U y ) 
 	reurn x+y;
/*
	这样的写法并不能通过编译。这是因为在编译器读到 decltype(x+y) 时,x 和 y 尚未被定
	义
*/
decltype ( x + y ) add(T x, U y )

// C++14 开始是可以直接让普通函数具备返回值推导
// 尾返回类型(trailing return type)
template <typename T , typename U >
auto add ( T x , U y ) -> decltype(x+y) 
 	reurn x+y;

  1. decltype(auto)
    decltype(auto) 主要用于对转发函数或封装的返回类型进行推导,无需显示的指定decltype的参数
string getString();
decltype(auto) get_string_test()
	return getString();

2.4 控制流

  1. if constexpr
template <typename T>
auto print_type_info(const T& t) {
    if constexpr (is_integral_v<T>) {
        return t + 1;
    } else {
        return t + 0.01;
    }
}
int main()
{
    cout << print_type_info(1) << endl;    // 2
    cout << print_type_info(1.0) << endl;     // 1.01

    return 0;
}
  1. 区间for迭代
vector<string> t;
for(string temp : t);
for(auto temp : t);

2.5 模板

C++ 模板能够将一切能够在编译器处理的事情在编译器处理,并且模板和泛型编程息息相关

  1. 外部模板
    模板在被使用的才会被编译器实例化,项目中每次使用到的模板在编译器被实例化,会造成代码的编译时间增加,
    C++11 引入外部模板,可以显示的通知编译器进行模板的实例化
template class vector<bool>; // 强行实例化
extern template class vector<double>; // 不在当前编译文件中实例化模板 
  1. 尖括号 “>”

位运算符>>
前期模板中的 vector<vector > 最后的>> 是需要程序员自己增加空格
C11 之后vector<vector> 变得合法化了

  1. 类型别名模板
// typedef  和 using
using callback = std::function<void(string,int)>;
typedef void(*callback)(string,int);
  1. 默认模板参数
template <class T = int, class TT = float>
auto add(T x, TT y) ->decltype(x+y)
	return x+y;
  1. 变长参数模板
 template < typename Require , typename ... Args > 
 class Magic {
public:
 	Magic(Args ...args){
		std :: cout << sizeof ...( args ) << std :: endl ;
	}	
 }
 

5.1 递归模板函数

template <typename T >
void printf(T value) {
   std::cout << value << std::endl;
   
} 
template < typename T, typename ... Args >
void printf(T value, Args ... args) {
   std::cout << value << std::endl;
   printf(args ...);

}

int main()
{
   printf(1, 2, "123 ", 1.1);
   return 0;
}

// C17
template < typename T0 , typename ... T >
void printf ( T0 t0 , T ... t ) {
   std :: cout << t0 << std :: endl ;
   if constexpr ( sizeof ...( t ) > 0) printf ( t ...) ;
}

5.2 初始化列表展开

template < typename T , typename ... Arg >
void printf ( T t ,  Arg... args ) {
   std :: cout << value << std :: endl ;
   return std :: initializer_list <T >{([&] {
   	std :: cout << args << std :: endl ;
   	}() , value ) ...};
}
print (1 , 2.1 , "123 ");

5.3 折叠表达式

// c17
template < typename ... T >
auto sum ( T ... t ) {
   return ( t + ...) ;
}
sum (1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10)

2.6 面向对象

  1. 委托构造
// 委托构造 简化代码
class Base {
public:
    int m_val;
    int m_data;
    Base() :m_val(m_val) {
    }
    Base(int data) : Base() {
        m_data = data;
    }
    Base(int val, int data) : Base(data) {
        m_val = val;
    }
};
  1. 继承构造
// 继承构造
class Base {
public:
    int m_val;
    std::string m_data;
    Base() {
        m_val = 1;
    }
    Base(std::string data) :Base() {
        m_data = data;
    }
};

class Subclass :public Base {
public:
    using Base::Base;

};

  1. 显式虚函数重载 override final virtual
struct Base {
    virtual void foo();
};

struct SubClass : Base {
    virtual void foo() override;
};


struct  BaseFinal {
	// 禁止被重载
    virtual void foo() final;
};
// 禁止被继承
struct SubClassFinal final : BaseFinal {
};
  1. 显式禁用默认函数
class Magic {
public:
    Magic() = default;
    Magic& operator=(const Magic&) = delete;
    Magic(Magic&) = default;
    Magic(Magic&&) = default;
    ~Magic() = default;
};
  1. 强类型枚举
enum class new_enum : unsigned int {
    value1,
    value2,
    value3 = 100,
    value4 = 200
};

template <typename T>
std::ostream& operator<<(typename std::enable_if<std::is_enum<T>::value,
    std::ostream>::type& stream, const T& e) {

    return stream << static_cast<typename std::underlying_type<T>::type>(e);
}


int main()
{
    cout << new_enum::value4 << endl;
    return 0;
}

3 语言运行期的强化

3.1 Lambda表达式

捕获列表 mutable(可选)->返回值类型{ 函数体};
捕获列表 值捕获= 引用捕获& 变量捕获 空捕获列表
C14 表达式捕获

   auto important = std::make_unique<int>(1);
   auto add = [v1 = 1, v2 = std::move(important)](int x, int y)->int{
       return x + y + v1 + (*v2);
   };

   cout << add(3, 4) << endl;

泛型 Lambda(C14)
auto add = [](auto x,auto y){ return x+y; }

3.2 函数对象包装器

lambda 表达式的本质是一个和函数对象类型相似的类类型的对象,当捕获列表为空的时候还可以转换为函数指针进行传递

  1. std::function
// C++11 std::function 是一种通用、多态的函数封装
using foo = std::function<void(int)>; // using foo = void(int);
void functional(foo f) {
    return f(1);
}

int main()
{
    auto f = [](int value) {
        cout << value << endl;
    };

    functional(f);
    f(1);

    return 0;
}
  1. std::bind/std::placeholder
int foo(int a, int b) {
    return a + b;
}

int main()
{
    auto bindFoo = std::bind(foo, std::placeholders::_1, 2);
    cout << bindFoo(1) << endl;
    return 0;
}
  1. 右值引用
/*
左值 右值的纯右值 将亡值 右值
左值
	表达式后依然存在的持久对象
右值
 	表达式结束后不存在的临时对象
纯右值
    字面量和匿名的临时对象 lambda表达式都是纯右值
将亡值
    即将被销毁,却能够被移动的值
 */
void reference(string& str) {
    cout << "左值" << endl;
}
void reference(string&& str) {
    cout << "右值" << endl;
}

int main()
{
    string lv1 = "string";
    string&& rv1 = std::move(lv1);
    const string& lv2 = lv1 + lv1;
    string&& rv2 = lv1 + lv1;
    reference(rv2);    // 左值
    return 0;
}

4.移动语义

class A {
public:
    int* m_ptr;
    A() :m_ptr(new int(1)) {
        cout << "构造" << "  " << m_ptr << endl;
    }
    A(A& a) :m_ptr(new int(*a.m_ptr)) {
        cout << "拷贝" << "  " << m_ptr << endl;
    }
    A(A&& a) :m_ptr(a.m_ptr) {
        cout << "移动" << "  " << m_ptr << endl;
    }
    ~A() {
        cout << "析构" << "  " << m_ptr << endl;
    }
private:

};
// 防止编译器优化
A return_rvalue(bool test) {
    A a, b;
    if (test)
        return a;
    return b;
}

int main()
{
    A obj = return_rvalue(false);
    cout << obj.m_ptr << endl;
    cout << *obj.m_ptr << endl;
    return 0;
}

/*
*  首先会在 return_rvalue 内部构造两个 A 对象,于是获得两个构造函数的输出;
*  函数返回后,产生一个将亡值,被 A 的移动构造(A(A&&))引用,从而延长生命周期,并将这个右值
*  中的指针拿到,保存到了 obj 中,而将亡值的指针被设置为 nullptr,防止了这块内存区域被销毁
*/

5.完美转发
完美转发就是在传递参数的时候保持原来的参数类型

void reference(int& v) {
    cout << "左值" << endl;
}

void reference(int&& v) {
    cout << "右值" << endl;
}
template <typename T>
void pass(T&& v) {
    reference(v);
    reference(std::move(v));
    reference(std::forward<T>(v));
    cout << endl << endl;
}
int main()
{
    pass(1);
    int v = 10;
    pass(v);
    return 0;
}

4.容器

4.1 std::array 和 std::forward_list

引入arrayforward_list 能够让代码变得更现代化,并且使代码更加安全,同时还能够友好的使用标准库中的容器算法, 使用array 的时候要兼顾C风格的接口

forward_list 底层是由单向链表进行实现;不提供size接口;

4.2 无序容器

有序容器map/set* 这些元素内部通过红黑树进行实现,
无序容器中的元素是不进行排序的,内部通过Hash 表实现,在不关心容器内部元素顺序时,能够获得显著的性能提升
unordered_map/unordered_mutimap/unordered_set/unordered_multiset

4.3 元组 tuple

元组的三个核心的函数
1.make_tuple: 构造元组
2.get : 获得元组某个位置的值
3.tie: 元组拆包

#include <tuple>

auto getStudent(int id) {
    if (id == 0) {
        return std::make_tuple(3.8, 'A', "张三");
    }
    if (id == 1) {
        return std::make_tuple(4.8, 'B', "李四");
    }
    return std::make_tuple(0.0, 'N', "null");
};

int main()
{

    auto student = getStudent(0);
    cout << "data: "
        << "  GPA: " << get<0>(student)
        << "  成绩: " << get<1>(student)
        << "  姓名: " << get<1>(student)
        << endl;
    double gpa;
    char grade;
    string name;

    std::tie(gpa, grade, name) = getStudent(1);
    cout << "data: "
        << "  GPA: " << gpa
        << "  成绩: " << grade
        << "  姓名: " << name
        << endl;
    return 0;


}

// 合并元组
auto newTuple = tuple_cat(getStudent(1),move(t));
// 遍历元组
template <typename T>
auto tupleLen(T &tpl){
	return std::tuple_size<T>::value;
}

for(int i=0;i!=tupleLen(newTuple);++i){
	cout << tuple_index(i,newTuple)<<endl;
}

5.指针

5.1 RAII 与引用计数

  • 引用计数是为了防止内存泄漏,
  • 在构造函数中申请内存,在析构函数中释放,也就是RAII 资源获取即初始化
  • C11引入智能指针,使用引用计数的想法,让程序中不在需要关注手动释放内存

5.2 shared_ptr

shared_ptr 内部使用引用计数,当引用计数为0的时候,就会将对象自动删除
// 初始化的两种方式
std::shared_ptrstd::string s1(new string(“123”));
std::shared_ptrstd::string s2 = std::make_sharedstd::string(“1234”);
// 获取对象的原始指针
string* origin = s2.get();
// 减少引用计数
s2.reset();
// 获取对象被引用的个数
s2.use_count();

5.3 unique_ptr

//std::unique_ptr是一种独占的智能指针,禁止其他智能指针与其共享同一个对象,从而保证代码的安全
std::unique_ptr<int> p = std::make_unique<int>(10);      // c14
// 移动权限
auto data = std::move(p);
// make_unique_ptr 实现机制
template <typename T, typename ...Args>
std::unique_ptr<T> my_make_unique(Args&& ...args) {
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

5.4 weak_ptr

// shared_ptr 循环引用问题
// 循环引用

struct A;
struct B;

struct B {
    using Ptr = std::shared_ptr<B>;
    A::Ptr _ptr;
    ~B() {
        cout << "~B()" << endl;
    }
};

struct A {
    using Ptr = std::shared_ptr<A>;
    B::Ptr _ptr;
    ~A() {
        cout << "~A()" << endl;
    }
};

int main()
{

    A::Ptr a = std::make_shared<A>();
    B::Ptr b = std::make_shared<B>();
    a->_ptr = b;
    b->_ptr = a;

    return 0;
}

在这里插入图片描述
// 解决这个问题的办法就是使用弱引用指针std::weak_ptr,弱引用不会引起引用计数增加
在这里插入图片描述

6.正则表达式

6.1 正则表达式简介

普通字符
特殊字符
限定符

6.2 std::regex 及其相关

7.线程与并发

7.1 thread

#include <thread>
void foo() {
    cout << "test thread" << endl;
}

int main()
{
    std::thread t(foo);
    t.join();

    return 0;
}

7.2 std::mutex, std::unique_lock

#include <thread>
#include <mutex>

#define LOCK_GURAD(x) std::lock_guard<decltype<x>> lk(x)

// 发生异常 锁没有被释放
void someOperator(const string& message) {
    static std::mutex mtx;
    LOCK_GURAD(mtx);
}
// unique_lock 

std::mutex mtx;
void block_area() {
    std::unique_lock<std::mutex> lock(mtx);
    
    lock.lock();
    lock.unlock();
}


int main()
{
    std::thread t(block_area);
    t.join();
    return 0;
}

7.3 std::future, std::packaged_task

/*
* std::future 提供了一个访问异步操作结果的途径;
* 问题: 试想我们的主线程A 希望开辟一个线程B 去执行某个我们预期的任务,并返回一个结果
* 解决方案: 线程A 中启动任务B,执行完成后发送一个事件执行结果保存在某个全局变量中,当线程A
*   想要执行结果时,调用一个线程等待的函数获取结果
* 
* std::future简化此流程,可以用来获取异步任务的结果,是一种线程同步的方法
* std::packaged_task 可以用来封装任何可以调用的目标,从而实现异步调用
* 
*/


int main()
{
    
    std::packaged_task<int()> task([]() {return 7; });
    std::future<int> result = task.get_future();
    std::thread(std::move(task)).detach();
    std::cout << "waiting...";
    result.wait();
    cout << "Done :" << result.get() << endl;
    return 0;
}

7.4 std::condition_variable

#include <thread>
#include <mutex>
#include <future>
#include <condition_variable>
#include <queue>
#include <chrono>

/*
 std::condition_variable 是为了解决死锁而生的,当互斥锁的引入而不够实现场景功能时
 问题: 线程A 可能等待某个条件为真才能继续执行,而一个忙等待循环中可能会导致其他所有线程都
    无法进入临界区使条件为真就会发送死锁,
 解决方法: condition_variable实例被创建出现主要就是用于唤醒等待线程从而避免死锁
 std::condition_variable的notify_one和notify_all 就是为了解决此问题

*/
// 


int main()
{
    // 生产者的数量
    std::queue<int> producedNums;
    std::mutex m;
    std::condition_variable condVar;
    // 结束标志
    bool done = false;
    // 通知标志
    bool notified = false;


    // 生产者线程
    std::thread producer([&] {
        for (size_t i = 0; i < 5; ++i) {
            std::this_thread::sleep_for(std::chrono::seconds(1));
            std::unique_lock<std::mutex> lk(m);
            cout << "producing  " << i << endl;
            producedNums.emplace(i);
            notified = true;
            condVar.notify_one();
            
        }
        done = true;
        notified = true;
        condVar.notify_one();
        });
    
    // 消费者线程
    std::thread consumer([&]() {
        std::unique_lock<std::mutex> lk(m);
        while (!done) {
        
            while (!notified) {
                // 循环避免虚假唤醒
                condVar.wait(lk);
                
            }
            while (!producedNums.empty()) {
                cout << " cosuming   " << producedNums.front() << endl;
                producedNums.pop();
            }
            notified = false;
        }
        });

    producer.join();
    consumer.join();
    return 0;
}

8. 文件系统

8.1 std::filesystem

9. 其他

9.1 新类型

cout << sizeof(long long int) << endl; // 8

9.2 noexcept 的修饰和操作

// noexcept 修饰完一个函数之后能够起到封锁异常扩散的功效,如果内部产生异常,外部也不会触发
void divid(int a,int b) {
    if (b == 0)
        throw std::invalid_argument("0");
}

void test() noexcept {
    return;
}

9.3 字面量

  1. 原始字符串字面量 去除转义限制
    std::string str = R"(C\User\Desktop)";
  2. 自定义字面量
/*
    
  自定义字面量支持四种字面量
  * 整形字面量:重载时必须使用 unsigned long long、const char *、模板字面量算符参数
  * 浮点型字面量:重载时必须使用 long double、const char *、模板字面量算符
  * 字符串字面量:必须使用 (const char *, size_t) 形式的参数表
  * 字符字面量: 参数只能是 char, wchar_t, char16_t, char32_t 这几种类型

*/

std::string operator"" _wow1(const char* wow1, size_t len) {
    return std::string(wow1) + "wooooooow, amazing";
}
std::string operator"" _wow2(unsigned long long i) {
    return std::to_string(i) + "wooooooow, amazing";
}

int main()
{
    auto str = "abc"_wow1;
    auto num = 1_wow2;
    cout << str << endl;
    cout << num << endl;

    return 0;
}

C20

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值