C++远征篇汇总
(一)C++远征之起航篇
初识C++
第一章. 初识C++
(一)
C++的起源:
1.诞生地:贝尔实验室(Bell Lab)
2.C++之父:比雅尼 • 斯特劳斯特鲁普博士(为人低调,有问必答)
C++的应用领域:1. 嵌入式,如手机,电视机等、2. 游戏编程 、3. 网络编程、4. 系统编程C++与C的关系:
1.从语法角度上来讲:C是C++的子集
2.从历史角度上来讲:C++ 是从C的基础上发展而来的, C语言面向过程, 而C++支持面向过程+支持面向对象
###### PS:c语言更接近低端语言,容易识别,故比C++运行更高效
3.学习C++需要C语言的基础,相关网址为:http://imooc.com/learn/249
C++ IDE环境搭建:
1.IDE环境:Integrated Development Environment(集成开发环境)一般包括代码编辑器、编译器、调试器和图形用户界面等工具、一般常使用的工具:Visual Studio 2010旗舰版、Visual Assist X for Visual Studio、Dev C++、CLion等
2.IDE初体验——Visual Studio 2019的使用中存在的问题:
安装问题:
为了图节省的硬盘空间,少下了一些东西,例如NET的移动开发,和个别C++ 程序应用过程中的必要负载,如C++桌面开发 ,针对已安装好,才后悔的人(例如我(~ ̄▽ ̄)~)我提供了以下解决方案:
首先,在该路径(C:\Program Files (x86)\Microsoft Visual Studio\Installer)中找一个叫setup的应用程序,也就是Visual Studio 2019的安装程序(一般这个路径是默认的,你就算在安装时换了安装路径,安装完以后,它依然在哪里);其次,打开后你可以看到在Visual Studio哪里有一个修改按钮,之后便可以进行修改,重新再来了。
闪跳问题:
在你的项目(project)上右击鼠标,在弹出菜单上选择最后一项“property/属性”,在左边的一栏里找到“配置属性->链接器->系统”,点击“系统”项;在右边的栏的“子系统(subSystem)”将刻项的值配置为"Console(控制台/SUBSYSTEM:CONSOLE)"。
C++的基本了解
-
-
新数据类型
新逻辑类型bool(true 和false):消除了C中只能用int类型(0或1)来判断真假的情况
-
新的初始化方法
- 复制初始化(同C) int x=1024
- 直接初始化 int x(1024)
-
随用随定义
-
输入和输出方式
- 输入:cin
- 输出:cout
-
Ps: oct、dec、hex分别是8进制、10进制、16进制的英文缩写,如输出一个八进制数(C++): cout<<oct<<x<<endl;输出bool值的符号:boolalpha)
-
- 命名空间(namespace)
即划片取名字
样例:
定义: 输出调用:
namespace A
cout<<A::x<<endl;
{
A::f1();
int x=0;
void f1();
void f2();
}
注意:命名空间的名字是不可以重复的,如使用cout和cin函数时,要包含iostream头文件和using namespace std;命名空间
(二) C++远征之离港篇
引用
即变量的一个别名,一个变量不可能只有别名,否则,别名即是该真名
变量类型的相关引用:
- int a=3; int &b=a;//引用必须初始化
结构体类型的相关引用: typedef struct{int x;int y;}Coor;
引用方式: Coor c1;(对象实例化)
Coor&c=c1
指针类型的引用
*&指针引用名=指针;
int *p=&a;(这里p取a的值,为10)
int *&q=p;```
###### 引用作函数参数
void fun(int a, int b) 在使用引用后:
{ void fun(int &a, int &b)
int c=0; {
c=*a;
int c=0;
*a=*b; c=a;
*b=c; a=b;
} b=c;}
在调用上: 在调用上:
int x=10,y=20; int x=10,y=20;
fun(&x,&y); fun(x,y);
***注意:引用不能单独存在,如int &a; int b;就是错的***
### const
###### ***起控制变量的作用,使目标变量成为一个常量,使其不可被改变***
- const与指针类型
- const int*(const)p=NULL;
- int const* (const) p=NULL
- int*const p=NULL; 限定了p
###### 补充:const int x=3;const int *const p=&x;//p=&y;*p=4;都是错误的
原因是,const使x和p都变成了常量,均不可被改变,也不可被变量引用
- const与引用
- int x=3;const int &y=x;//x=10;正确 //y=20;错误
### 一些函数新特性
- > 函数参数默认值
- 有默认参数值的参数必须在参数表的最右端
如:void fun(int i,int j=5,int k=10);
- 无实参则用默认值,否则实参覆盖默认值
- > 函数重载:***在相同作用域内,用同一函数名定义的多个函数,且在多个函数中参数个数和参数类型是不同的***
>
> 例如:int getMax(int x, int y, int z){ //to do} /double getMax(double x,double y){//to do}
> 计算机编译好后形成的新的参数:
> getMax_int_int_int
> getMax_double_double
- **优势**:1) 提高了代码命名时的准确性和高效性;2) C++的重载的两个函数参数数量可以相同也可以不同,当参数数量相同时,只需要对应参数类型不同即称为重载
#### 内联函数 (编译时将函数体代码和实参代替函数调用语句)
> **内联函数关键字:inline**
> 例子:
inline int max(int a,int b,int c);
int main()
{
int i=10,j=20,k=30,m;
m=max(i,j,k);
代码展开:
int a,b,c;
a=i;b=j,c=k;
if(b>a) a=b;
if(c>a) a=c;
m=a;
cout<<"max="<<m<<endl;
return 0;
}
减少了2和4的过程
***注意点:***
->内联编译是建议性的,由编译器决定
->逻辑简单,调用频繁的函数建议使用内联
->递归函数无法使用内联方式
### 内存管理
***申请/归还内存资源就是内存管理***
*******
***内存的申请和释放***
内容总结
> 使用new申请内存,使用delete释放内存
> 申请内存需要判断是否成功,释放内存需要设空指针
> new与delete配套使用
- 申请内存
运算符new :
int p=new int(20);/ int arr=new int[10];
注意:int *p = new int [20]; delete []p;申请的是空间数组,而
int *p = new int (20); delete p;申请的是内存空间
***释放内存***
- 运算符delete : ```delete p;/ delete[]arr;```
- 申请内存注意事项:
int *p=new int [1000];
if(NULL==p){//内存分配失败}
## C++远征之封装篇
- ### 封装篇(上)
- ##### 类和对象
- > 类的定义
关键字->class Dog<——类名
{
char name[20];
int age; <——数据成员(属性)
int type;
void speak();
void run(); <————————成员函数(方法)
};
类是抽象的概念,对象是具体的事物
定义类应当使用关键字class
###### ***目的不同抽象出的信息不同(选择性暴露,即封装)***
> 访问限定符
public: 公共的
protected: 受保护的
private: 私有的
> 对象实例化
从栈实例化
从堆实例化
##### 初识string
字符串类型:string
栗子:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string name="ZhangSan";
string hobby("football");
cout<<name<<hobby<<endl;
return 0;
}
###### 拓展:1)检验用户输入是否为空时,要使用getline函数;2)name.empty()函数,判断名字是否为空;3)name.size()函数,判断名字的长度
##### 数据的封装
>面向对象的基本思想: 对象的所有行为都通过调用函数完成
>定义在类里面的变量规范,如string类型的,写为m_strName; int类型的,写为:m_iName
- ##### 类外定义
###### ***类内定义与内联函数:即成员函数和函数体写在类的里面,类内定义的函数优先选择编译为内联函数***
###### ***类外定义:即成员函数和函数体写在类的外面,它分为:同文件类外定义和分文件类外定义***
##### 构造函数
内存分区
栈区: int x=0; int *p=NULL;
堆区: int *p = new int[20];
全局区: 存储全局变量及静态变量
常量区: string str = "hello";
代码区:存储逻辑代码的二进制
###### ***对象初始化***
有且仅有一次
根据条件初始化
构造函数的规则和特点
- 构造函数在对象实例化时被自动调用
- 构造函数与类同名
- 构造函数没有返回值
- 构造函数可以有多个重载形式
- 实例化对象时仅用到一个构造函数
- 当用户没有定义构造函数时,编译器自动生成一个构造函数
无参构造函数
class Student
{
public:
Student(){m_strName = "jim";}
private:
string m_strName;
};
默认构造函数
###### ***即在实例化对象时不需要传递参数的函数, 一个类可以没有默认构造函数,有别的构造函数也可以实例化对象***
构造函数初始化列表
###### ***初始化列表***
class Student
{
public: Student():m_strName("Jim"),m_iAge(10){}
private:
string m_strName;
int m_iAge;
};
初始化列表特性
初始化列表先于构造函数执行
初始化列表只能用于构造函数
初始化列表可以同时初始化多个数据成员
初始化列表存在的必要性
有参构造函数
class Student
{
public:
Student(string name)
{m_strName = name;}
private:
string m_strName;
};
参数带默认值
参数无默认值
###### ***重载构造函数***
class Student
{
public:
Student(){m_strName = "jim";}
Student(string name)
{m_strName = name;}
private:
string m_strName;
};
##### ***系统自动生成的函数***
拷贝构造函数
定义格式:类名(const 类名&变量名)
class Student
{
public:
Student(){m_strName = "jim";}
Student(const Student& stu){}
private:
string m_strName;
};
注意点:
###### 1)如果没有自定义的拷贝构造函数则系统自动生成一个默认的拷贝构造函数
###### 2)当采用直接初始化或复制初始化实例化对象时,系统自动调用拷贝构造函数
###### 3)拷贝构造函数是确定的,不能重载
##### ***普通构造函数***
析构函数
定义格式: ~类名()
class Student
{
public:
Student() {m_pName = new char[20];}
~Student() {delete [] m_pName;}
private:
char *m_pName;
};
如果没有自定义的析构函数则系统自动生成
析构函数在对象销毁时自动调用
析构函数没有返回值、没有参数也不能重载
### 封装篇(下)
对象成员与对象数组
对象数组
定义:
class Coordinate
{
public:
int m_iX;
int m_iY;
};
使用时:
int main(void)
{
Coordinate coord[3]; //栈上
coord[1].m_iX=10;
Coordinate *p = new Coordinate[3]; //堆中
p[0].m_iY = 20; p->m_iY = 20;
delete []p;
p = NULL;
return 0;
}
注意点:
1. 堆中实例化的数组需要手动销毁释放内存,在栈中实例化的数组,系统自动回收内存
2. 实例化对象数组时,每一个对象的构造函数都会被执行。
3. 实例化对象数组时,内存既可以从堆上分配,也可以从栈上分配。
4. 销毁对象数组时,每一个对象的析构函数都会被执行。
对象成员
###### ***注意点***
1. 坐标类的构造函数是需要有参数的
2. 对象成员在调用构造函数的顺序是从里往外,在调用析构函数的时候是从外往里
点的定义
class Coordinate
{
public:
Coordinate(int x, int y);
private:
int m_iX;
int m_iY;
};
线段的定义
class Line
{
public:
Line();
private:
Coordinate m_coorA;
Coordinate m_coorB;
};
#### ***深拷贝与浅拷贝***
##### ***浅拷贝***
例子:
class Array
{
public:
Array(){m_iCount = 5;}
Array(const Array& arr)
{m_iCount=arr,m_iCount;}
private:
int m_iCount;
};
int main(void)
{
Array arr1;
Array arr2=arr1;(在这里,arr1=m_iCount,而arr2=arr,m_iCount;)
return 0;
}
浅拷贝所带来的劣势:
当拷贝指针类型的相关函数时,会使得拷贝的函数与原先的函数指向同一个地址,进而可能造成系统的崩溃
##### ***深拷贝***
小知识:快速注释掉代码的快捷键:Ctrl+k+c(适用于Visual Studio)
对象指针
即用指针来指向一个对象
int main(void)
{
Coordinate *p=new Coordinate;
p->m_iX=10; //(*p).m_iX=10;
p->m_iY=20; //(*p).m_iY=20;
delete p;
p=NULL;
return 0;
}
对象成员指针
定义:
class Line
{
public:
Line();
~Line();
private:
Coordinate *m_pCoorA;
Coordinate *m_pCoorB;
};
内存中的对象成员指针
一个指针占4个内存单元,一共占了八个
###### ***this指针***
用于解决参数与数据成员同名所引起的系统报错问题
class Array
{
public:
Array(int len){this->len=len;}
int getLen(){return len;}
void setLen(int Len){this->len=len;}
private:
int len;
};
this指针在参数列表中的位置
##### this指针的相关注意点
###### 1. this指针无需用户定义,是编译器自动产生的。
###### 2. 对象各自的this指针指向各自对象的首地址,所以不同对象的this指针一定指向不同的内存地址
###### 3. 当成员函数的参数或临时变量与数据成员同名时,可以使用this指针区分同名的数据成员。
###### 4. this指针也是指针类型,所以在32位编译器下也占用4个基本的内存单元,即sizeof(this)的结果为4。
#### const之再现江湖
##### 常对象成员和常成员函数
###### 常对象成员
class Line
{
public:
Line(int x1,int y1,int x2,int y2);
private:
const Coordinate m_coorA;
const Coordinate m_coorB;
};
常成员函数
class Coordinate
{
public:
Coordinate(int x,int y);
void changeX() const; <——常成员函数
void changeX();
private:
int m_iX;
int m_iY;
}
常指针与常引用
常指针
const Coordinate *pCoor=&coor1;
常引用
const Coordinate &coor2=coor1;
###### ***注意点***
1. 一个对象可以有多个对象常引用
2. 常指针和常引用都只能调用对象的常成员函数
3. 普通对象能够调用常成员函数,也能够调用普通成员函数
4. 常对象只能调用常成员函数,不能调用普通成员函数
## C++远征之继承篇
### 什么是继承
class Worker: public Person
{
public:
void work();
int m_iSalary;
};
其中:
派生类(子类)——>class Worker
基类(父类)——>public Person
继承的相关注意点
1. 被继承的类叫做基类也叫做父类,从其他类继承而来的类叫做派生类也叫做子类。
2. 子类中不仅继承了父类的中的数据成员,也继承了父类的成员函数。
3. C++中的继承关系是概念上的父子关系,不是个体的父子关系。
4. 类与类之间必须遵循概念上的父子关系,否则将造成定义和使用的混乱
### 继承方式
公有继承
class A: public B
保护继承
class A: protected B
私有继承
class A: private B
###### 相关注意点
1. B类从A类派生,那么B类是A类的子类,A类是B类的超类。
2. B类从A类派生,那么B类中含有A类的所有数据成员。
3. B类从A类公共派生,那么通过B类的对象只能调用到A的公共及保护限定符下的成员函数
4. B类从A类公共派生,那么可以在B类中直接使用A的公共及保护限定符的数据成员。
### 继承中的特殊关系
隐藏
关键字:父子关系、成员同名、隐藏
Is-a
存储结构
虚析构函数
###### 应用条件:当存在继承关系时,我们需要使用父类的指针去只指向堆中的子类的对象,并且还想使用父类的指针去释放堆中的内存 virtual ~Person();
### 多继承与多重继承
多重继承
class Person
{
};
class Soldier: public Person
{
};
class Infantryman: public Soldier
{
};
多继承
class Worker
{
};
class Farmer
{
};
class MigrantWorker: public Worker, public Farmer
{
};
注意点
1. 多继承是指一个子类继承多个父类
2. 多继承对父类的个数没有限制,继承方式可以是公共继承、保护继承和私有继承。
3. 多重继承与多继承不同,当B类从A类派生,C类从B类派生,此时成为多重继承。
### 虚继承
关键字:virtual
使用方法:
class Worker: virtual public Person
{
};
class Farmer: virtual public Person
{
};
小知识:用以解决重定义的相关bug
#ifndef 文件名称,如:#ifndef PERSON_H
#define 文件名称,如:#define PERSON_H
..........
#endif
## C++远征之多态篇
### 虚函数及实现原理
虚函数
多态
什么是多态
指相同对象收到不同消息或不同对象收到相同消息时产生不同的动作
分类
静态多态(早绑定)
动态多态(晚绑定)
virtual->虚函数
实例
class Shape
{
public:
virtual double calcArea() //虚函数
{
cout<<"calcArea"<<endl;
return 0;
}
};
相关注意点
1. 多态具体到语法中是指,使用父类指针指向子类对象,并可以通过该指针调用子类的方法。
2. 产生多态的基础是继承关系,没有继承就没有多态
3. 多态的语法核心是virtual关键字,必须使用virtual才能使多个类间建立多态关系。
4. 封装、继承、多态是面向对象的三大特性。
虚析构函数
多态中存在的问题
内存泄露
int main(void)
{
Shape *shape1=new Circle(3,5,4.0);
shape1->calcArea();
delete shape1;
shape1=NULL;
return 0;
}
###### virtual在函数中的使用限制
1)普通函数不能是虚函数
2)静态成员不能是虚函数
3)内联函数不能是虚函数
4)构造函数不能是虚函数
###### 注意点
1. 虚函数使用virtual关键字定义,但使用virtual关键字时,并非全部是虚函数
2. 虚函数特性可以被继承,当子类中定义的函数与父类中虚函数的声明相同时,该函数也是虚函数。
3. 虚析构函数是为了避免使用父类指针释放子类对象时造成内存泄露。
virtual->析构函数
实现原理
函数指针
理论前提:执行完子类的析构函数就会执行父类的析构函数
###### 注意点
1. 在C++中多态的实现是通过虚函数表实现的
2. 当类中仅含有虚析构函数,不含其它虚函数时,也会产生虚函数表
3. 每个类只有一份虚函数表,所有该类的对象共用同一张虚函数表
4. 两张虚函数表中的函数指针可能指向同一个函数。
### 纯虚函数和抽象类
### 运行时类型识别
### 异常处理
## C++远征之模板篇
### 友元函数和友元类
友元函数
###### 关键字:friend
友元全局函数
class Coordinate
{
friend void printfXY(Coordinate &c);
public:
Coordinate(int x,int y);
private:
int m_iX;
int m_iY;
};
友元成员函数
class Coordinate
{
friend void Circle::printXY(Coordinate &c);
public:
Coordinate(int x,int y);
private:
int m_iX;
int m_iY;
};
友元类
class Circle;
class Coordinate
{
friend Circle;
public:
Coordinate(int x,int y);
private:
int m_iX;
int m_iY;
};
劣势:易破坏函数的封装性,且不易被察觉
关于友元的注意事项
1) 友元关系不可传递
2) 友元关系的单向性
3) 友元声明的形式及数量不受限制
4) 友元只是封装的补充
### 静态
###### 关键字:static
普通数据成员与静态数据成员的区别
静态数据成员是依赖于类的,而普通数据成员则是依赖于对象的
###### 注意事项
1. 静态数据成员必须单独初始化
2. 静态成员函数不能调用非静态成员函数和非静态数据成员
3. 静态数据成员只有一份,且不依赖对象而存在
### 运算符重载
给原有运算符赋予新功能
关键字:operator
一元运算符重载
实际上就是只与一个操作数进行运算
二元运算符重载
### 模板函数与模板类
函数模板
关键字: template 、 typename 、 class
例子
template<class T>
T max(T a, T b) <——函数模板
{
return( a>b )? a:b;
}
模板函数:
int ival=max(100, 99);
char cval =max<char>('A', 'B');
类模板
定义
template<class T>
class MyArray
{
public:
void display()
{......}
private:
T*m_pArr;
};
类外定义
template<class T>
void MyArray<T>::display()
{
.......
}
使用
int main(void)
{
MyArray<int> arr;
arr.display();
return 0;
}
###### 注意事项
1. 定义一个类模板就相当于定义了一系列功能相同类型不同的类
2. 定义类模板需要使用关键字template
3. 定义类模板的参数可以使用typename和class,可以混用
4. 模板参数既可以是类型,也可以是变量
### 标准模板类
###### STL
本质:对数组的封装
特点:读取能在常数时间完成
###### 注意点
1. vector是对数组的封装,大小可以根据元素数量改变
2. map需要与pair一起使用,用来存储多个key-value对。
3. 不同厂商的标准模板库的实现细节可以不同,基本用法及原理相同。
###### 暂停函数:system("pause");
调用的函数库:include<stdlib.h>