一、C++基础语法学习
1.常量指针 : const int * b : 指针的地址可以修改,但是内容不可以修改
2.指针常量 : int * const b : 指针的内容可以修改,但是地址不可以修改
3.引用 : int a = 20 ; int &b = a;
a. 引用必须初始化
b. 引用一旦初始化就不能发生改变
c. 引用本质是 : 指针常量,地址不可以改变,数据可以改变
4.std::uint64_t : unsigned long long
std::uint32_t : unsigned int
std::uint16_t : unsigned short
std::uint8_t. : unsigned char
5. 将字符串转为long、int 、float类型
String —> 无符号long : std::stoull
Strng —-> 无符号int : std::stoui
Strng —-> 无符号float : std::stouf
6. 转意符
std::string str1 = "45";
int myint1 = std::stoi(str1);
std::cout << "std::stoi(\"" << str1 << "\") is " << myint1 << '\n';
输出结果 : std::stoi("45") is 45
7. C++ 数据类型转换
static_cast : 静态类型转换,明确把隐式转换变为显示转换,便与可读性
reinterpreter_cast : 重新解释类型,强制类型转换
dynamic_cast : 用于虚函数基类与派生类之间引用和对象的转换
const_cast : 去const属性
8. C++函数模版的机制
编译器会对函数模版进行两次编译,第一次是对函数模版本身编译,第二次是在运行时候参数替换后编译
9.智能指针学习
auto_ptr :
缺点:在编译时无法检测出来,再运行时才报错。创建形式是通过拷贝构造函数和复值,函数运行时会出现空指针
unique_ptr :
优点:拷贝构造函数和复值函数都被delete,禁止使用拷贝构造函数和复值函数创建对象
shared_ptr : 强指针
优点:可以使用构造函数和复值函数创建对象
weak_ptr : 弱指针
拓展知识:
shared_from_this : 可以返回一个shard_ptr指针,里面是有weak_ptr实现的
weak_ptr.lock() 可以拿到一个shared_ptr指针
shared_ptr线程安全性 : 多个线程同时读同一个shared_ptr对象是线程安全的,但是如果是多个线程对同一个shared_ptr对象进行读和写,则需要加锁
10. move变量 : 将左值转换为右值,将A对象的所有权转移到B对象,只是转移,没有内存拷贝,A然后被释放,B获得A所有的能力
二、C++多线程学习
C++11多线程支持thread、atomic、mutex、condition_variable、future5个关键库
thread : 线程类
atomic : 原子操作
atomic :
atomic_flag :
mutex : 互斥量
mutex : 基本Mutex类
lock()、unlock()、try_lock()
recursive_mutex : 递归Mutex类,递归上锁
timed_mutex : 定时Mutex类
try_lock_for : 等一段时间是否可以获得锁,获得返回为true,没有获得返回为flase
try_lock_until : 等到某个时间是否可以获得锁,获得返回为true,没有获得返回为flase
recursive_timed_mutex : 定时递归Mutex类
lock_guard : 当多个线程抢占资源时候,其中一个锁在异常情况下无法释放,通过这种方式,可以释放锁,防止死锁现象的放生
使用方法:
mutex mMutex;
lock_guard sk1(mMutex);
unique_lock : 它具有lock_guard一切能力,也可以控制解锁和非解锁更加灵活的控制使用范围
使用方法:
mutex mMutex;
unique_lock sk2(mMutex);
sk2.lock();
sk2.unlock();
condition_variable : 条件变量,可以做到不同线程通信
condition_variable : 需要搭配unique_lock使用
condition_variable_any : 可以搭配任何mutex使用,但效率比condition_variable低
常用方法:
wait : 阻塞自己,等待唤醒
wait_for : 等某一段时间,一直阻塞自己,等待唤醒
wait_until : 等到某个时间,一直阻塞自己,等待唤醒
notify_one : 唤醒某一个 阻塞线程
notify_all : 唤醒所有阻塞线程
future : 异步操作,可以拿到不同线程的结果
Futures类的使用:
int sum(int a, int b) {
sleep_for(chrono::seconds(5));
return a + b;
}
future result = async(sum, 123, 456);
result.wait(); //等待结果算出
int sum = result.get(); //得到结果
Promise类:
使用方法:
void work(promise<int> &prom) {
sleep_for(chrono::seconds(5));
//设置结果值
prom.set_value(6666);
}
promise<int> pro;
future result = pro.get_future();
thread t1(work, ref(pro));
t1.detach();
int sum = result.get();
cout<< "输出结果:" << sum<< endl;
三、数据结构
1. vector : 一块连续、单向开口、动态数组(myFirst、myLast、myEnd三个指针)
2. deque : 一块连续、双向开口、动态数组(核心:中控器)
3. list : 一块非连续、非顺序的双向链表
4.set和multiset : 底层都是红黑树作为实现
4. map和multimap : 底层都是红黑树作为实现
四、C++面试总结
1.vector : 因为vector使用数组实现的,因此元素是保存在连续的内存中的,所以通过索引取值的速度非常快
size的大小:结束的数据-开始的数据
capacity的大小:申请一块连续的内存大小
2.resize()、reserve()两个函数对比
resize : 影响的存储的数据的大小,也有可能影响capacity这个大小,因为size长度比capacity的长度大,这个时候就需要扩容
reserve:它只影响capacity的大小,不会影响size的大小
扩容时候会把之前旧数据拷贝到新的数据里面,然后再把老的数据清除掉
3. list : 在底层使用一个双向的环形链表实现的,所以在任意位置进行增加或者删除速度都比较快,都是指针交换
4.Map、Multimap、Unordered_map、Unordered_multimap
Map和Multimap:底层基于红黑树,元素自动有序,且插入、删除效率高,时间复杂度是O(logN)
Unordered_map和Unordered_multimap : 底层基于哈希表,故元素无序,查找效率高,时间复杂度是O(1)
5.迭代器和指针的区别
迭代器(iterator)概念 : 对容器里面的数据进行遍历
迭代器:对指针里面的功能进行一系列封装的指针,它是一个类模版。
指针:它是一个存放对象的地址的指针变量。
常用设计模式:单例模式、外观模式、适配器模式、责任链模式、装饰者模式、模版模式、工厂模式、组合模式、代理模式、观察者模式、策略模式、建造者模式
6.模版 :template作为关键字
类模类和函数模版的区别
类模类:调用时候类时候参数类型必须是显性,指定好类型
函数模版:调用时候函数时候参数可以显性或隐性,指定好类型或者不指定类型
7.友元函数,需要用friend来作为定义
类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。
8.指针和引用的区别
引用创建时候同时被初始化,引用不能为null,指针可以在任何场景下初始化,指针可以是null
引用一旦初始化就不能修改,指针随时可以改变
引用的sizeof取决于变量的大小,指针的sizeof的大小是4个字节
引用有编译检查,指针无编译检查
如果返回动态内存分配的对象或者内存,必须使用指针, 不使用引用(可能引起内存泄露)
9.虚函数用sizeof来看大小
虚函数用sizeof大小是4,(32位计算器)虚函数指针的大小是4,指针用sizeof去计算大小都是4
10.虚函数和纯虚函的区别
虚函数:它是一个继承,子类不一定需要实现,继承只能实现一个,构造函数不能使用虚函数
纯虚函数:它是一个接口,子类必须实现,它是在方法后面加个=0,接口可以实现多实现
什么情况下需要将析构函数定义为虚函数?
当一个类里面有虚函数时候,继承之后就会调用该方法,这样会导致内存泄漏,才必须再析构函数加virtual这个关键字
11.struct和class的区别
class默认访问是private,而struct默认访问是public
实例化的class存在堆内存中,而实例化的struct存在栈中,它不用执行析构函数,执行效率更高
class可以继承,而struct不能继承
12.什么是左值、什么是右值
左值 : 变量是左值,可以放在左边和右边
右值 : 常量是右值,只能放在右边
13. include的""和<>区别
#include“”优先从自己定义的源文件中查找,找不到之后才去标准库文件中查找。
#include<>优先从引入的标准库文件中查找。<>里面一般都放标准库.h。
14.inline内联函数的作用以及缺点
作用:代码过短但被经常调用,需要使用内联函数,这样可以大大提高运行效率
缺点:方法不能过长,也不能有循环语句在里面
不使用inline内联函数:
int add(int a, int b) {
return a + b; ------------> (内联函数)
}
使用inline函数: inline void add(int a , int b)
15. 智能指针
智能指针有:auto_ptr(自动)、shared_ptr(共享)、weak_ptr、unique_ptr(独占)
auto_ptr :
shared_ptr : 共享指针(强指针)
weak_ptr : 弱指针
unique_ptr : 独占指针
作用:申请的空间在函数结束时忘记释放,类会自动调用析构函数,防止造成内存泄漏
shared_ptr的原理:
初始化以及增加情况
1.第一次创建shared_ptr时候,对象计数和引用计数设置为1
2.当shared_ptr赋值给新的shared_ptr的时候,对象计数+1,引用计数不变
3.当shared_ptr赋值给新的weak_ptr的时候,对象计数不变,引用计数+1
4.当weak_ptr获取shared_ptr的时候,对象计数不变,引用计数+1
减少以及销毁情况
1.当shared_ptr对象析构时候,对象计数-1,引用计数不变,当对象计数为0时候,SharedPtr释放内存,引用计数-1。
2.当weak_ptr对象析构时候,对象计数不变,引用计数-1,当引用计数为0时候,*refCount释放内存
16.const的关键字的作用
作用:使内容和变量只有读的功能
作用范围:
变量、指针:该函数只具有读的功能,const修饰的变量一定要在构造函数内部初始化
函数:表明是一个常函数,里面的函数的变量不能修改
17.深拷贝和浅拷贝的区别
浅拷贝:只是对指针进行拷贝,最终两个指针指向还是同一块地址
深拷贝:不只是对指针进行拷贝,里面内容也会一起拷贝,最终两块指针只向不同的地址
18.static的作用
作用:数据共享,减小内存,数据存放在静态区,可以保持封装特性,避免命名冲突的问题,不能和const连用,因为static里面没有this这个对象
19.volatile的作用
数据不是写到寄存器里面,而是直接写在内存中,表现的是可见性,只是限制编译器优化的作用,表现的是禁止指令重排序
20.数组指针和指针数组
数组指针定义: int (*a)[5] , 它是一个指针,指向一个包含5个int数组的首地址
指针数组定义: int * a[5] ,它是一个数组,里面存着5个指针
区别:指针数组的sizeof是取决于数组的长度,数组指针的sizeof是4个字节
21.面向对象的特征
继承、封装、多态
22.赋值构造函数 : 又称拷贝构造函数
使用场景:
a、一个对象以值传递方式传入函数体
b、一个对象以值传递的方式从函数返回
c、一个对象通过另一个对象进行初始化
好处:防止没有执行a、b之后,一个对象通过另一个对象初始化,就会导致一个指针指向已经删除的空间
23.内存的分配
静态存储区域:负责存储静态成员
栈存储区域: 负责存储临时变量或者局部变量
堆存储区域:负责存储new malloc等创建的动态生成的对象
24.new delete 和 malloc free的区别
a、malloc/free 是c语言中的库函数,需要头文件支持;而new/delete 是c++中的关键字
b、malloc需要显式指出所需内存的大小,new是通过编译器内部计算
c、malloc内存分配成功时,返回的是void*,需要转换成所需要的类型,new内存分配成功时,返回的是对象类型的指针
d、free只是负责释放内存,delete是调用析构函数再释放内存
25. const和#define的区别
const有数据类型,而#define没有数据类型
const是在编译过程进行检查,而#define是预编译阶段(#代表预编译),会比较安全
26. 重载、重写、隐藏(重定义)的区别
重载:在同一个类,方法名一样,参数不同或者返回类型不同
重写:在父类、子类中,父类的抽象方法在子类实现,子类和父类方法完全一致
隐藏:
a、在父类、子类中,父类的方法在子类实现,子类和父类方法完全一致(没有抽象方法)
b、在父类、子类中,父类的抽象方法在子类实现,子类重载父类
27. 构造函数为什么不能是虚函数?
a. 从空间角度分析:vtable指针是存储在对象的内存空间的,构造函数如果是虚函数,对象还没有实例化,也就说内存空间还没有,所以不能构造函数不能为虚函数
b. 从使用角度分析:虚函数可以继承,如果子类继承父类的构造方法,就不能正确识别对象类型从而不能正确调用析构函数
28.堆内存和栈内存的区别
a.栈内存由操作系统分配,堆内存由程序员自己分配。
b.栈内存空间大小是固定的,堆的大小受限于系统中有效地虚拟内存。
c.栈的空间由系统决定合适释放,堆内存需要自己决定何时释放。
d.堆的使用容易产生碎片,但是使用方便。
29. 内存对齐
#pragma pack(n) , 默认为#pragma pack(4)个字节对齐
30. explicit 的关键字
用来防止由构造函数定义的隐式转换
31.C++中拷贝构造函数的参数为何一定要用引用类型
首先可以减少一次内存拷贝,其次就是防止创建无限递归下去
32.数组和指针的区别
a. 数组的sizeof的大小取决于里面变量多少,指针sizeof大小为4
b. 数组不能操作++或者--,指针可以执行++或者--
c. 数组不能随意改地址,指针随意可以改地址
33.set和map的实现以及区别
实现:
set和map都是基于红黑树实现的
区别:
a. map中的元素是key-value形式,set只有key的数值
b. map支持下标操做,set不支持下标操做
so存放位置:
naviengine/build/intermediates/cmake/release/
addr2line存放位置:
/Users/名字/Library/Android/sdk/ndk/16.1.4479499/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-addr2line -e (so存放的位置) 000b0eae
玄马教育:
https://space.bilibili.com/477729104/video?tid=0&page=1&keyword=&order=pubdate
多线程讲解:
https://space.bilibili.com/147020887/channel/seriesdetail?sid=1745830
C++中文学习网址:
https://zh.cppreference.com/w/cpp
CMake网址:
https://www.bookstack.cn/read/CMake-Cookbook/README.md
C++显示汇编的编程网址:
https://godbolt.org/
TutorialsPoint: 高级语言文档教程:
https://www.tutorialspoint.com/cplusplus/index.htm
Awesome C++ : 常用框架使用
https://link.zhihu.com/?target=https%3A//github.com/fffaraz/awesome-cpp
explicit用来防止由构造函数定义的隐式转换
网友评论