30小时快速精通C++

原文链接:http://www.jianshu.com/p/746e291f5190

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"修饰,函数实现可以不修饰

1062649-0a7d758995bb32f2.png
ExternC.png

#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汇编

1062649-a18888108263d1dc.png
汇编.png

读汇编代码最简语法:

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

1062649-4114b61ce64692b8.png
sington.png

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的私有属性
}

运算符重载函数

1062649-fd94d2ade2a511b1.png
operator.png

模板的使用格式如下

1062649-ebe5ae336b3bc277.png
template.png

template <typename\class T> //typename和class是等价的

1、模板没有被使用时,是不会被实例化出来的

2、模板的声明和实现如果分离到.h和.cpp中,会导致链接错误

3、一般将模板的声明和实现统一放到一个.hpp文件中

面向对象:五大原则

1.单一职责

一个类只做单一的事情,方便单元测试

2.开闭原则

对扩展开放(不需要修改原来的代码),修改封闭

3.替换原则

一个类可以直接替换它的派生类,父类的方法和声明,不应该更改,父类能实现的,子类也应该能实现

4.接口隔离

父类的有些接口不需要子类知道,就把接口隔离,不隔离子类实现了不需要的接口,父类改变子类也需要跟着改变

5.依赖倒置

依靠抽象类来编程,外部的接口没变,内部的实现改变了
展开阅读全文

没有更多推荐了,返回首页