【C++进阶】01:概述

概述


C语言 C++语言
面向过程编程
面向对象编程(类和对象)
泛型编程、元编程(模板)
函数式编程(Lambda)
STL(function、bind、thread、智能指针)
其他(左值与右值、深拷贝、移动构造、返回值优化)
异常处理
设计模式(创建型模式4种、结构型模式7种、行为型模式7种)

在这里插入图片描述

C++11新特性:

  1. auto关键字
  2. decltype关键字
  3. nullptr字面值
  4. constexpr关键字
  5. for (declaration : expression)
  6. Lambda表达式
  7. initializer_list(初始化列表)
  8. 标准库bind函数
  9. 智能指针shared_ptr, unique_ptr
  10. 右值引用&&(C++性能得到非常大的提升)
  11. STL容器std::array, std::forward_list, std::unordered_map, std::unordered_set

C++14新特性:

  1. 扩展了lambda表达式,增加泛型:支持auto
  2. 扩展了类型推导至任意函数:C11只支持lambda返回类型的auto
  3. 弃用关键字deprecated

C++17新特性:

  1. 扩展了constexpr至switch if等:C++11的constexpr函数只能包含一个表达式
  2. typename嵌套
  3. inline内联变量
  4. 模板参数推导
  5. 元组类std::tuple\std::pair实现两个元素的组合
  6. 类模板std::variant表示一个类型安全的联合体
  7. 引用包装器std::reference_wrapper
  8. 边长参数模板
  9. 结构化绑定(函数多值返回时用{}合成struct)
  10. 非类型模板参数可传入类的静态成员
  11. 在if和switch中可进行初始化
  12. 初始化(如struct)对象时可用花括号对其成员进行赋值
  13. 简化多层命名空间的写法
  14. lambda表达式可捕获*this的值,但this及其成员为只读
  15. 十六进制的单精度浮点数
  16. 继承与改写构造函数
  17. usingB1::B1;//表示继承B1的构造函数
  18. 当模板参数为非类型时,可用auto自动推导类型
  19. 判断有没有包含某文件__has_include

C++20新特性:

  1. concept用于声明具有特定约束条件的模板类型

    template<typename T> concept number = std::is_arithmetic<T>::value; //声明一个数值类型的concept

  2. 范围库Ranges Library

  3. 协程Coroutines

  4. 模块Modules

C++程序编译过程

在这里插入图片描述

  1. 预处理Preprocessing:将.cpp文件转化为.i文件,cpp -o test.i test.cppgcc -E test.c -o test.i

    预处理器把所有include的文件包括递归包含的文件内容,都展开到输出文件,并展开了所有的宏定义。

  2. 编译Compilation:将.cpp/.h文件转换成.s文件,cc test.i -o test.sgcc -S test.i -o test.x

    编译的过程将预处理的文件进行一系列的词法分析、语法分析、语义分析及优化成相应的汇编代码。这一步中一般会进行优化,比如去除没有用到的类的声明、循环语句的优化等。

  3. 汇编Assemble:将.s文件转化为.o文件,as -o test.o test.sgcc -c test.s -o test.o

    as汇编器会将汇编代码转换为机器指令,并以特定的二进制格式输出保存在目标文件中

  4. 链接Linking:将.o文件转换为可执行程序,ld test.o -o testgcc test.c

    ld链接器将程序的相关目标文件组合链接在一起,生成程序的可执行映像文件。要解决的问题是:可能调用了库函数、或者一个目标文件中调用了另外一个文件中的库函数,需要通过链接器建立对应的关系,使程序能够正常的执行。

补充:静态链接与动态链接

  • 静态链接:将源代码从静态链接库中拷贝到最终的可执行程序中,这样可能会导致最终的目标文件很大。
  • 动态链接:需要调用的库函数以动态链接库的形式存在,多个进程之间共享。而链接的时候只需要知道要调用的函数的位置即可。在程序执行时当需要调用某个动态链接库中的函数式,操作系统首先会查找所有正在运行的程序,看内存中是否已经有该库函数的拷贝了,如果有则多进程之间可以共享该拷贝,否则才会将其载入到该进行的虚拟内存中。

C++内存模型

C++程序内存分为5个区:堆、栈、静态全局区、常量区、代码区

在这里插入图片描述

  • 堆:new/malloc创建的内存,堆在内存中位于bss区和栈区之间,用于动态内存分配,一般由程序员分配和释放。
  • 栈:函数中的临时变量(局部变量)、函数的参数值,由编译器自动分配释放,
  • 全局区:声明变量既不在函数中也不再类中,分为未初始化和已初始化全局变量,存放静态数据、常量,程序结束后由系统释放。
  • 只读常量区:存放常量字符串,程序结束后由系统释放
  • 代码区:函数的定义、类定义、相关的程序逻辑(函数体的二进制代码)

补充:静态分配与动态分配

  • 静态分配:指发生在程序编译和链接的阶段
  • 动态分配:发生在程序的运行阶段

C++STL

如果对SLT不够熟练,在企业开发当中将会导致开发效率十分低,

1.Queue&Stack

#include<iostream>
#include<queue>
using namespace std;

int main() {
    
    
    queue<int> q;
    q.push(1);
    q.push(2);
    q.push(3);
    
    cout << "front = " << q.front() << " back = " << q.back() << " size = " << q.size() << endl;
    q.pop();
    cout << "front = " << q.front() << " back = " << q.back() << " size = " << q.size() << endl;
    q.pop();
    cout << "front = " << q.front() << " back = " << q.back() << " size = " << q.size() << endl;
    q.pop();
    cout << "front = " << q.front() << " back = " << q.back() << " size = " << q.size() << endl;
    q.pop();
    return 0;
}

在这里插入图片描述

可以看到虽然队列中只push了3次元素,但是使用queue进行第4次pop时程序也没有报错提醒,并且back的值居然得到了结果。

#include<iostream>
#include<stack>
using namespace std;

int main() {
    
    
    stack<int> s;
    s.push(1);
    s.push(2);
    s.push(3);

    cout << "top = " << s.top() << " size = " << s.size() << endl;
    s.pop();
    cout << "top = " << s.top() << " size = " << s.size() << endl;
    s.pop();
    cout << "top = " << s.top() << " size = " << s.size() << endl;
    s.pop();
    cout << "top = " << s.top() << " size = " << s.size() << endl;
    s.pop();
    return 0;
}

在这里插入图片描述

栈中只push了3次元素,使用stack进行第4次pop时程序直接给出了segmentfault错误。

  • 总结:在使用queue和stack等容器时,一定要做有效性的校验,否则拿到的数据可能会出错,或者直接导致segmentfault.
if (!q.empty()) cout << "front = " << q.front() << " back = " << q.back() << " size = " << q.size() << endl;
else cout << "queue is empty!" << endl;
if (!s.empty()) cout << "top = " << s.top() << " size = " << s.size() << endl;
else cout << "stack is empty!" << endl;

2.String

  1. str1 == str2调用的是str1的方法还是str2的方法?(运算符重载)

    答:理解为调用的是类对象str1的函数,而str2作为参数传入函数中,str1是一个this指针(this对象是str1)

  2. str1=“abcdefg”; str1[7]=‘K’;不报错,而str1[20]=‘K’;报错segmentation fault的原因?

    答:string对象会进行自动扩容1.5~2倍(编译器决定)

    #include<iostream>
    #include<string>
    using namespace std;
    
    int main() {
          
          
        string str1 = "helloworld";
    
        cout << "str1 = " << str1 << " size = " << str1.size() << endl;
        cout << "str1[0] = " << str1[0] << endl;
        cout << "str1[9] = " << str1[9] << endl;
        cout << "str1[10] = " << str1[10] << endl;
        cout << "str1[11] = " << str1[11] << endl;
        cout << "str1[12] = " << str1[12] << endl;
        cout << "str1[13] = " << str1[13] << endl;
        cout << "str1[20] = " << str1[20] << endl;
        cout << "str1[50] = " << str1[50] << endl;
        cout << "str1[120] = " << str1[120] << endl;
    
        int n = 1000;
        while (n--) str1 += "T";
        cout << "str1 = " << str1 << " size = " << str1.size() << endl;
        return 0;
    }
    

    在这里插入图片描述

  3. str1.length()与strlen(str1)的区别?

    答:strlen的时间复杂度为O(n),而string.length的时间复杂度为O(1),string类中存储着length的大小,strlen为遍历方式

    #include<iostream>
    #include<string>
    #include<cstring>
    using namespace std;
    
    int main() {
          
          
        int n = 100000000;
        string s1;
        while (n--) s1 += 'T';
    
        long start = clock();
        int size = s1.size();
        cout << "c++ size = " << size << " time = " << clock() - start << endl;
    
        start = clock();
        size = strlen(s1.c_str());
        cout << "c size = " << size << " time = " << clock() - start << endl;
    
        return 0;
    }
    

    在这里插入图片描述

    很明显的计算时间的差异

  4. cout对字符串的输出结束判定是通过str.size()的,而不是像C语言一样通过字符串末尾的\0

  5. 直接使用 str[11] = T 等赋值操作无法像 += 操作一样新增string字符串内容并触发string.size的修改。

3.Map

Map 头文件 命名空间
hash_map <hash_map>/<ext/hash_map> __gnu_cxx;
unordered_map <unordered_map> std
  1. 映射过程是根据哈希函数进行映射的,结果是乱序的,hash_map底层是利用数组实现的,在数组上再挂在链表。

  2. 使用map时需要预先进行find查找,以免访问未分配空间造成size增加!

    #include<iostream>
    #include<unordered_map>
    using namespace std;
    
    int main() {
          
          
    	unordered_map<string, int> hashmap;
    	hashmap["abc"] = 1;
    	hashmap["abcd"] = 2;
    	hashmap["abcde"] = 3;
    	hashmap.insert(pair<string, int>("adcdef", 4));
    	cout << "size = " << hashmap.size() << endl;
    
    	cout << "hashmap[\"hello\"] = " << hashmap["hello"] << endl;//访问非法空间则自动生成一个默认的类型
    	cout << "size = " << hashmap.size() << endl;
    	return 0;
    }
    

    在这里插入图片描述

    auto it = hashmap.find("hello");
    if (it != hashmap.end()) cout << "hashmap[\"hello\"] = " << hashmap["hello"] << endl;
    else cout << "hashmap[\"hello\"] is not exist!" << endl;
    
  3. 当map集合非常大时,使用swap方法比直接使用运算符重载的=进行赋值更快!

    swap方法将两个数据集进行对换,对换的过程中没有出现内存的拷贝,速度比使用重载的赋值运算符更快。

    #include<iostream>
    #include<map>
    using namespace std;
    
    int main() {
          
          
    	map<int, int> mymap1;
    	int n = 10000;
    	while (n--) mymap1[n] = 2;//map集合的数据集非常大10000条数据
    
    	unsigned long tick = clock();
    	map<int, int> mymap2 = mymap1;
    	cout << "time(operator) = " << clock() - tick << endl;//直接使用运算符重载时消耗的时间
    
    	tick = clock();
    	map<int, int> mymap3;
    	mymap3.swap(mymap1);
    	cout << "time(switch) = " << clock() - tick << endl;//使用switch方法对换数据消耗的时间
    
    	return 0;
    }
    

    在这里插入图片描述

  4. 遍历hashmap

    int main() {
          
          
    	unordered_map<string, int> hashmap;
    	hashmap["abc"] = 1;
    	hashmap["abcd"] = 2;
    	hashmap["abcde"] = 3;
    	hashmap.insert(pair<string, int>("adcdef", 4));
    	cout << "size = " << hashmap.size() << endl;
    
    	auto it = hashmap.find("hello");
    	if (it != hashmap.end()) cout << "hashmap[\"hello\"] = " << hashmap["hello"] << endl;
    	else cout << "hashmap[\"hello\"] is not exist!" << endl;
    
    	for (auto it = hashmap.begin(); it != hashmap.end(); ++it) {
          
          
    		cout << "key = " << it->first << "\tvalue = " << it->second << endl;
    	}
        for (auto it : hashmap) {
          
          
    		cout << "key = " << it.first << "\tvalue = " << it.second << endl;
        }
    	return 0;
    }
    

    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_49167174/article/details/130040495