1.const
放在成员函数末尾,只读,增强文章可读性
常量修饰符
限制方法修改数据
定义变量时是否需要初始化
2.const与指针
int a=8;
const int *p=&a;
int *const p=&a
左定值,右定址,const 修饰不变量
3.inline
编译关键字,用于简单函数,由编译器决定是否将具体函数替换使用
与普通函数相比,减少了函数调用时的开销,不用寻址,
4.explicit
使用后拒绝隐式转换,防止单参构造函数的隐式转换
5.析构函数的调用??
delete调用
6. 什么是友元函数
非成员函数但能使用成员变量
7.c++运算符重载
只能重载c++中存在的运算符
左侧为成员对象
8. 强制类型转换(显示类型转换)与隐式类型转换
强转:由大到小, 语法:函数型和类c型
隐式转换: 由小到大,不需要用户干预,编译器私下进行的类型转换,explicit拒绝隐式转换
9.深复制与浅复制
10.c++类
自动提供的成员函数
默认构造函数
默认析构函数
复制构造函数
应该的原型:
Class_name(const Class_name &);
然而默认复制函数,确是值复制,即浅复制
赋值构造函数
与复制构造函数相同:
Class_name& Class_name::operator=(const Class_name &);
地址构造函数
什么时候调用拷贝构造,什么时候调用赋值操作
前者: 用对象初始化对象,对象以值传递的方式传递给函数,函数局部对象以值传递的方式从函数返回
后者:对象赋值给对象
如果在类中自定义了构造函数,则默认构造就会失效
11.虚函数
1,关键字:virtual
2,如果使用指向对象的引用或指针来调用虚方法,程序将使用对象类型定义方法,而不使用为引用和指针类型定义的方法,
是否能解决多继承中的二义性问题?
二义性问题,分为:
-
路径二义性,倒三角,
解决办法:1.作用域限定符 基类::
2.子类重写父类方法 -
菱形二义性,菱形
1.作用域限定符 基类::
2.子类重写父类方法
3.虚基类,虚继承
3,如果定义的类将被用作基类,则应将那些要在派生类中重新定义的类方法声明为虚方法
虚析构函数的意义:
防止内存泄漏,->拓展:
什么是内存泄漏,内存溢出
内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。
内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。
原理:
[C++]虚析构函数的作用 - zhizhiyu - 博客园 (cnblogs.com)
虚析构函数使得在删除指向子类对象的基类指针时可以调用子类的析构函数达到释放子类中堆内存的目的,而防止内存泄露的。
12.new与delete运算符
new一个自定义对象时,会调用对象的构造函数,delete则会调用析构函数
- 不要使用delete来释放不是new分配的内存
- 不要使用delete释放同一个内存块两次
- 如果new[]为数组分配内存,则应使用delete[]来释放
- 如果使用new为一个实体分配内存,应使用delete来释放
-
对空指针应用delete是安全的
int *a = new int(10); //动态创建整型数,无参数是 * a=0,有参数则 * a = 参数
int *p = new int[10]; //创建一个有10个元素的动态整型数组,没有赋值,元素为随机数
int *p = new int[10] (); //创建一个有10个元素的动态整型数组,并都赋值为0
面试题
malloc/free与new/delete的区别:
1.操作符与函数
2.空间是否能初始化
3.malloc的返回值必须强制类型转换,因为是(void*)型
4.申请自定义对象内存空间时,malloc/free只能开空间,而后者能调用对应函数操作
5.malloc申请失败返回NULL,new抛出异常
6.malloc申请需要手动计算空间大小
13.指针
int *pt=new int;
pt 指针
*pt等同于一个int类型的变量
14.c++11新特性,
1.lambda表达式
[外部变量访问方式说明符](参数)mutable noexcept/throw()->返回值类型{函数体;}
[=]: 以值传递的方式,导入所有外部变变量,只能读
[&]: 以引用传递的方式,导入所有外部变量,能读能写
2.自动类型推导
auto使用时必须对变量初始化
禁用:
auto 不能用在函数参数中
auto 不能用于类的非静态成员变量
auto 关键字不能定义数组
auto 不能用于模板
3.decltype
自动类型推导,返回表达式类型,语法:decltype(expression)
4.列表初始化(初始化统一)
c++
vector<int>{1,2,3,5};
std::unordered_map<int,std::string>m={
{1,"one"},
{2,"two"},
{3,"three"}
}
5.右值引用
int a=10;
a是左值,
10是右值
int &&r1=13;
&&实现右值引用声明
右值分类:
- 纯右值: 非引用返回的临时变量(返回的变量,函数头没有&修饰,为将亡右值)、运算表达式产生的临时变量、原始字面量和 lambda 表达式等
- 将亡值: 与右值引用相关的表达式,比如,T&& 类型函数的返回值、 std::move 的返回值等。std::move(对象)会调用移动构造
右值与左值的转换
左值到右值 std::move()
右值不能转化为左值,但右值能通过赋值给右值引用提高生命周期
使用场景:移动语义,完美转发
- 移动构造的作用: 例如返回一个对象时有可能会调用复制函数,调用移动复制效率更高,在编码时,作为一个内存偷取得过程
c++
Matrix(Matrix &&Matr) { // 五法则移动构造:当内存管理对象使用右值构造,保证内存管理资源不被冗余使用
size_ = Matr.size_;
data = Matr.data;
Matr.data = nullptr;
}
- 完美转发:函数模板可以将自己的参数"完美"地转发给内部调用的其他函数,所谓完美,即不仅·能准确地转发值,还能保证参数得左值,右值属性不变
6.using 关键字新特性
using 新类型=就类型
7.C++11 标准引入了2个新特性:"=default" 和 "=delete"。
- 程序员只需在函数声明后加上"=default;",就可将该函数声明为 "=default"函数,编译器将为显式声明的 "=default"函数自动生成函数体。
- 程序员只需在函数声明后上"=delete;",就可将该函数禁用。程序员只需在函数声明后上"=delete",就可将该函数禁用
8.final与override继承控制关键字
final 与override都是放在被修饰函数后,
final(最终)修饰函数,子类不能重写,final修饰类,不能不能被继承
子类重写父类的方法的声明中,将override放在函数后,必须重写
9.智能指针
类似于指针的类对象
auto_ptr
unique_ptr
//上面两个同属于所有权模型
shared_ptr
//要想使用以上对象需使用#include<memory> 头文件
什么时候被释放
share_ptr采用引用计数的方法,当引用计数为0时调用析构函数
实现原理
1.unique_ptr 类中把拷贝构造函数,与拷贝赋值声明为private或delete,这样就不可以对指针指向进行拷贝,也就不能产生指向同一个对象的指针
10.闭包
定义:。。。
实现: lambda表达式
仿函数
std::bind (用于给一个可调用对象赋值,生成新的可调用对象)
std::function包装器 (可用function<void(int,int)>func=(int a,int b){}对一个lambda表达式进行具名)
15.c++中&运算符的新涵义
引用是已定义的变量的别名
&a =>可解释为对变量a起别名
16. 类型转换运算符
++ dynamic_cast
是否可以安全地将对象的地址赋给特定类型的指针,true,返回对象指针,否则,返回空指针
const_cast
(可能有点理解错误)应该是把const 修饰变量暂时变成可修饰的,妈的太复杂,别用了吧
++ static_cast
使用static_cast可以明确告诉编译器,这种损失精度的转换是在知情的情况下进行的,也可以让阅读程序的其他程序员明确你转换的目的而不是由于疏忽。
reinterpret_cast
17.友元类
友元类能直接使用基类的私有部分
18.模板
作用于通用编程,有类模板,函数模板,复杂的要死
类模板的声明与定义只能写在一个.h头文件
20.typename 关键字
当你想告知编译器iterator是类型而不是变量,只需要用typename:
21.c++中的多态
解释多态的含义
多态分为静态多态和动态多态,
1.静态多态的实现:函数重载,运算符重载,泛型编程
2.动态多态:在程序运行时根据基类的引用指向的对象来确定自己具体该调用哪一个类的虚函数,
表现效果:同样的调用语句在实际运行时有多种不同的表现效果
22.静态链接库和动态链接库
23.gdb调试工具的简单使用
调试的目标文件是可执行文件(.c文件编译后的文件)
编译时需要用gcc -g testgdb.c -o testgdb.exe
gcc -g testgdb.cpp -o testgdb.exe调加编译信息
1.启动
gdb 文件
2.打断点
b 行号
查看断点信息
info br
3.运行程序
run [args]
3.调试
n (next) 把函数当成指令 回车同理
s (step) 进入函数
c 执行到下一个断点或结束
4.查看
print
print &d //地址
char buf[20]="hello world";
print buf //输出字符串
x/s buf //输出字符串
int data[3]={2,3,4};
print data //输出数组所有数据
5.退出
quit
24.c++抽象类接口
纯虚函数
只能声明,没有实现
- 定义形式:
virtual void funcName( ) =0; - C++中包含纯虚函数的类,被称为是"抽象类"。抽象类不能使用new出对象,只有实现了这个纯虚函数的子类才能new出对象。
25.内存模型
26.c++对象的实例化方式
方式一:
c++
Person p1;
Person p1=Person();
方法调用:
p1.functionName();
方式二:
C++
Person *p1=new Person();
p1->functionName();
区别:
- 内存位置不同
前者在栈中,后者在堆中 - 内存分配有所差异
前者创建即分配,后者对象无初始化,不分配内存
27.写时赋值
如果有多个调用者(callers)同时请求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者
自我理解:如果有AB两个对象完全相同,我完全可以在内存中只创建一份对象,将两个指针指向同一内存资源,需要修改时,再去复制一份专用的副本,给修改的调用者
string类在c++11前运用过此项技术,c++11后为多线程放弃了
28. struct与class的区别
默认的访问权限,
ps:struct自定义类型,编译器自动为其生成默认构造函数
29.子类是否必须重写父类的虚函数
父类的纯虚函数必须重写,
父类的虚函数可以选择性重写