struct和class的区别
1、struct的默认成员权限是public
2、class的默认成员权限是private
C++中可以使用struct、class来定义一个类
C++编程规范
1、全局变量:g_
2、成员变量:m_
3、静态变量:s_
4、常量:c_
extern “C”,C++在调用C语言API时,需要使用extern "C"修饰C语言的函数声明
背景: C++编译器默认会对符号名(变量名、函数名等)进行改编、修饰, 重载时会生成多个不同的函数名,不同编译器(MSVC、g++)有不同的生成规则
1、被extern "C"修饰的代码会按照C语言的方式去编译
2、如果函数同时有声明和实现,要让函数声明被extern "C"修饰,函数实现可以不修饰
#pragma once
pragma once可以防止整个文件的内容被重复包含,#ifndef、#define、#endif可以针对一个文件中的部分代码,而#pragma once只能针对整个文件
C++允许函数设置默认参数,在调用时可以根据情况省略实参。
1、默认参数只能按照右到左的顺序
2、如果函数同时有声明、实现,默认参数只能放在函数声明中
3、默认参数的值可以是常量、全局符号(全局变量、函数名)
内联函数:
使用inline修饰函数的声明或者实现,可以使其变成内联函数,建议声明和实现都增加inline修饰
特点:
1、编译器会将函数调用直接展开为函数体代码
2、可以减少函数调用的开销
3、会增大代码体积
引用&
1、引用相当于是变量的别名(基本数据类型、枚举、结构体、类、指针、数组等,都可以有引用)
2、在定义的时候就必须初始化,一旦指向了某个变量,就不可以再改变,“从一而终”,可以利用引用初始化另一个引用,相当于某个变量的多个别名 int age =20; int &rAge = age;
3、不存在【引用的引用、指向引用的指针、引用数组】
4、引用存在的价值之一:比指针更安全、函数返回值可以被赋值
5、const引用 可以接受const和非const实参(非const引用,只能接受非const实参),可以跟非const引用构成重载
this是指向当前对象的指针,可以利用this.m_age来访问成员变量么?不可以,因为this是指针,必须用this->m_age
AT&T汇编 vs Intel汇编
读汇编代码最简语法:
1、 mov dest, src //将src的内容赋值给dest,类似于dest = src
2、 lea dest, [ 地址值 ] //将地址值赋值给dest,类似于dest = 地址值, [ 地址值 ],中括号[ ]里面放的都是内存地址
3、call 函数地址 //调用函数
4、 ret //函数返回
5、word是2字节,dword是4字节(double word),qword是8字节(quad word)
6、 jmp 内存地址 //跳转到某个内存地址去执行代码,j开头的一般都是跳转,大多数是带条件的跳转,一般跟test、cmp等指令配合使用
汇编算术运算:
1、 xor op1, op2 //将op1和op2异或的结果赋值给op1,类似于op1 = op1 ^ op2
2、 add op1, op2 //类似于op1 = op1 + op2
3、 sub op1, op2 //类似于op1 = op1 - op2
4、 inc op //自增,类似于op = op + 1
5、dec op //自减,类似于op = op – 1
封装的目的
成员变量私有化,提供公共的getter和setter给外界去访问成员变量
内存空间的布局
每个应用都有自己独立的内存空间,其内存空间一般都有以下几大区域
1、代码段(代码区)
✓ 用于存放代码
2、数据段(全局区)
✓ 用于存放全局变量等
3、栈空间
每调用一个函数就会给它分配一段连续的栈空间,等函数调用完毕后会自动回收这段栈空间自动分配和回收
4、堆空间
需要主动去申请和释放
在程序运行过程,为了能够自由控制内存的生命周期、大小,会经常使用堆空间的内存
申请堆空间成功后,会返回那一段内存空间的地址
堆空间的申请\释放
malloc \ free
new \ delete
new [] \ delete []
memset
Person person;
memset(&person,0,sizeof(person));
构造函数(Constructor)
1、一旦自定义了构造函数,必须用其中一个自定义的构造函数来初始化对象
2、通过malloc分配的对象不会调用构造函数
析构函数(Destructor)
1、无参,不可以重载,有且只有一个析构函数
2、通过malloc分配的对象free的时候不会调用析构函数
3、构造函数、析构函数要声明为public,才能被外界正常使用
命名空间可以用来避免命名冲突
其他编程语言的命名空间,Java->Package,Objective-C,类名前缀
命名空间可以用来避免命名冲突
namespace ZLY{
int g_age;
};
using namespace ZLY;
int main()
{
ZLY::g_age =20;
}
命名空间可以嵌套
namespace ZLY{
namespace::CC
{
}
}
::g_age =30; //有个默认的全局命名空间,我们创建的命名空间默认都嵌套在它里面,
多态
1、默认情况下,编译器只会根据指针类型调用对应的函数,不存在多态
2、同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果
3、在运行时,可以识别出真正的对象类型,调用对应子类中的函数,子类重写父类的成员函数(override),父类指针指向子类对象,利用父类指针调用重写的成员函数
C++中的多态通过虚函数(virtual function)来实现
1、虚函数:被virtual修饰的成员函数
2、只要在父类中声明为虚函数,子类中重写的函数也自动变成虚函数(也就是说子类中可以省略virtual关键字)
3、虚函数的实现原理是虚表,这个虚表里面存储着最终需要调用的虚函数地址,这个虚表也叫虚函数表
4、所有的Cat对象(不管在全局区、栈、堆)共用同一份虚表
虚析构函数
含有虚函数的类,应该将析构函数声明为虚函数(虚析构函数)
delete父类指针时,才会调用子类的析构函数,保证析构的完整性
纯虚函数
纯虚函数:没有函数体且初始化为0的虚函数,用来定义接口规范
抽象类(Abstract Class)
1、含有纯虚函数的类,不可以实例化(不可以创建对象)
2、抽象类也可以包含非纯虚函数
3、如果父类是抽象类,子类没有完全实现纯虚函数,那么这个子类依然是抽象类4、如果子类继承的多个父类都有虚函数,那么子类对象就会产生对应的多张虚表
静态成员(static)
静态成员:被static修饰的成员变量\函数
可以通过对象(对象.静态成员)、对象指针(对象指针->静态成员)、类访问(类名::静态成员)
静态成员变量
1、存储在数据段(全局区,类似于全局变量),整个程序运行过程中只有一份内存
2、对比全局变量,它可以设定访问权限(public、protected、private),达到局部共享的目的
3、必须初始化,必须在类外面初始化,初始化时不能带static,如果类的声明和实现分离(在实现.cpp中初始化)
class Car{
static int ms_count;
}
int Car::ms_count =0;
静态成员函数
1、内部不能使用this指针(this指针只能用在非静态成员函数内部)
2、不能是虚函数(虚函数只能是非静态成员函数)
3、内部不能访问非静态成员变量\函数,只能访问静态成员变量\函数
4、非静态成员函数内部可以访问静态成员变量\函数
5、 构造函数、析构函数不能是静态
6、 当声明和实现分离时,实现部分不能带static
const成员变量
必须初始化(类内部初始化),可以在声明的时候直接初始化赋值
class Car{
const int mc_wheelCount =20;
void run() const{
}
}
const成员函数(非静态)
1、const关键字写在参数列表后面,函数的声明和实现都必须带const
2、内部不能修改非static成员变量
3、内部只能调用const成员函数、static成员函数
4、非const成员函数可以调用const成员函数
5、const成员函数和非const成员函数构成重载
6、非const对象(指针)优先调用非const成员函数
7、const对象(指针)只能调用const成员函数、static成员函数
引用类型成员
引用类型成员变量必须初始化(不考虑static情况),在声明的时候直接初始化,通过初始化列表初始化
拷贝构造函数
当利用已存在的对象创建一个新对象时(类似于拷贝),就会调用新对象的拷贝构造函数进行初始化
class Car{
int m_price;
public:
//拷贝构造函数的格式是固定的,接收一个const引用作为参数
Car(const Car &car){
this->m_price =car.m_price;
}
}
explicit禁止隐式构造
C++中存在隐式构造的现象:某些情况下,会隐式调用单参数的构造函数,可以通过关键字explicit禁止掉隐式构造
Test1(int n)
{
num=n;
}//普通构造函数
Test1 t1=12;//隐式调用其构造函数,成功
class Test2
{
public:
explicit Test2(int n)
{
num=n;
}//explicit(显式)构造函数
private:
int num;
}
Test2 t2=12;//编译错误,不能隐式调用其构造函数
Test2 t2(12);//显式调用成功
友元包括友元函数和友元类
1、 如果将函数A(非成员函数)声明为类C的友元函数,那么函数A就能直接访问类C对象的所有成员
2、如果将类A声明为类C的友元类,那么类A的所有成员函数都能直接访问类C对象的所有成员
3、友元破坏了面向对象的封装性,但在某些频繁访问成员变量的地方可以提高性能
class Point{
friend Point add(const Point &,const Point &);
private:
int m_x;
int m_y;
}
Point add(const Point &p1,const Point &p2)
{
//访问p1,p2的私有属性
}
运算符重载函数
模板的使用格式如下
template <typename\class T> //typename和class是等价的
1、模板没有被使用时,是不会被实例化出来的
2、模板的声明和实现如果分离到.h和.cpp中,会导致链接错误
3、一般将模板的声明和实现统一放到一个.hpp文件中
面向对象:五大原则
1.单一职责
一个类只做单一的事情,方便单元测试
2.开闭原则
对扩展开放(不需要修改原来的代码),修改封闭
3.替换原则
一个类可以直接替换它的派生类,父类的方法和声明,不应该更改,父类能实现的,子类也应该能实现
4.接口隔离
父类的有些接口不需要子类知道,就把接口隔离,不隔离子类实现了不需要的接口,父类改变子类也需要跟着改变
5.依赖倒置
依靠抽象类来编程,外部的接口没变,内部的实现改变了