## 换行
endl与\n都表示换行
string 表示字符串
````cpp
string mytext="Hello";
````
## 常量
const 表示常量
声明后不可修改
````cpp
const a=10;
a=12;
cout<<a; 输出a=10;
#define b 6
cout<<b; 输出b=6;
````
## 关键字
int double string auto bool等为关键字,在声明变量时,不可以用关键字作为变量
## 标识符(变量、常量名)命名规则
1.不可以是关键字
2.由字母、数字、下划线构成
3.标识符第一个字母只能是字母或下划线
4.标识符是区分大小写的
## 数据类型
### 整型
1、short 短整型 2byte -2^15~2^15-1 -32768 ~ 32767
2、int 整型 4byte -2^31~2^31-1
3、long 长整型 windows 4byte linux 32位4byte 64位8byte
4、long long 长长整型 8byte -2^63~2^63-1
sizeof() 可以统计数据类型所占的内存大小
### 浮点型(实型)
1、float 4byte 7位有效数组
2、double 8byte 15~16位有效数字
编译器会默认小数为double型 float型会在数字后面加f
float a=3.14f;
小数最多表示6为有效数字,如果要查看更多的有效数字,需要配置
科学记数法表示小数:
float s2=3e2 如果e后是正数,代表3*10^2=3*10*10=300;
float s2=3e-2 如果e后是负数,代表3*0.1^2=3*0.1*0.1
### 字符型
char a='a'; 创建字符型变量要用单引号,且只能有一个字符
### 字符串
string str="hello world"
输出时,需要包含 <string> 头文件
### 逻辑运算
&& 与
| 或
! 非
### 函数重载情况下 如果体语句有默认值,会有二义性,要避免默认值
如
void func(int a,int b=10);
void func(int a);
如果主函数内调用时
func(a),则两个函数都符合,所以要避免这种情况
### 内存
#### 四区
代码区:存放函数的二进制代码,由操作系统进行管理
代码区是共享 只读的
全局区:存放全局变量 静态变量 常量
全局变量 static静态变量 常量 const修饰的(全局)变量 字符串常量
在全局区,这个取余的数据由操作系统来管理释放
栈区:由编译器自动分配释放,存函数参数值 局部变量等
不要返回局部变量的地址,
int func()
{
int a=10;
return &a;
}
int main()
{
int *p=func();
}
cout<<*p;
cout<<*p;
这段代码打印时,只有第一次可以正常打印,因为局部变量在释放时,编译器会保留一次,但是第二次就不再保留了
堆区:由程序员分配释放 若程序员不释放,则结束时由操作系统回收
new 可以在堆区开辟数据
int *p= new int(10);
new 返回的是一个数据类型的指针
new 开辟数组
int *arr= new int[20];//数组内有10个元素,返回值为线性空间的首地址
delete可以手动释放内存
delete p;
delete[] arr;
## 面向对象的三大特征 :封装 集成 多态
封装的意义:
1、将属性和行为作为一个整体
2、将属性和行为加以权限控制
访问权限:
1、 public
2、 protected 儿子可以访问父亲的保护内容
3、 priavte 儿子也不可以访问
class如果不标注 默认权限为私有权限
struct 默认权限为公共权限
### 类和对象
#### 构造函数和析构函数
构造函数:用于在创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无需手动调用
析构函数:用于在对象销毁前系统自动调用,执行一些清理工作。
如果不写构造函数和析构函数,编译器会提供,但是为空实现(没有内容)
由编译器自动调用一次
语法:
无返回值,函数名与类名相同,可以有参数,
Cube()
{
cout << "构造函数的调用" << endl;
}
无返回值,函数名与类名相同,前加一个 ~ 不可以有参数,所以不会发生重载
~Cube()
{
cout << "析构函数的调用" << endl;
}
#### 构造函数的分类和调用
按参数分为: 有参构造和无参构造(默认构造函数)
按类型分为: 普通构造和拷贝构造
//拷贝构造函数
Person(const Person &p)
{
}
//拷贝构造函数的调用时机
//1.使用一个已经创建完毕的对象来初始化一个新对象
//2.值传递的方式给函数参数传值
//3.值方式返回局部对象
三种调用方式:括号法 显示法 隐式转换法
显示法
Person p1=Person(10);
Person(10); 被称为匿名对象 特点:当前行执行结束后,系统直接回收掉
默认情况下,c++编译器至少会给一个类添加4个函数
1.默认构造函数(无参)
2.默认析构函数
3.拷贝构造函数,对属性进行值拷贝
4.赋值运算operator=对属性进行值拷贝
如果用户定义有参构造函数,则编译器不再提供无参构造
用户定义拷贝构造函数,编译器不再提供其他构造函数
深拷贝 和 浅拷贝
浅拷贝:简单的赋值操作
深拷贝:在堆区申请空间,进行拷贝操作
如果浅拷贝,则会引起堆区内存重复释放
如果属性有在堆区开辟的,则需要自己写拷贝函数
初始化列表
Person():属性(),属性() ...
{
}
#### 类的对象作为成员
class A{}
class B{
A a;
}
当其他类的对象作为本类成员,构造时,先构造本类的对象,再构造自身, 先构造A,再构造B
析构时,先析构本身,在析构对象 先析构B,再析构A
#### 静态成员
在成员变量和成员函数前加上关键词static 称为静态成员
静态成员变量:
所有对象共享同一份数据
在编译阶段分配内存(全局区)
类内声明,类外初始化
静态成员变量不属于某个对象,所有对象共享一份数据
因此有两中访问方式
1、通过对象访问
p.m_a;
2、通过类名访问
Person::m_a;
静态成员变量也有访问权限
私有权限类外访问不到
静态成员函数
所有对象共享一个函数
静态成员函数只能访问静态成员变量,因为无法分清是哪个对象的变量
只有非静态的成员变量才属于对象
空对象的内存空间是一个字节 分配这一个字节是为了区分空对象在内存的位置
非空对象的内存空间 就是非静态成员变量的大小,原先1内存的空对象大小会被释放
静态成员变量(函数)的空间不会被记入对象,
因为静态成员变量(函数)不属于类对象
非静态成员函数也不属于类对象上
只有非静态成员变量数类的对象
#### this指针
只有非静态成员变量数类的对象
静态成员变量和成员函数以及非静态成员函数都不属于类对象
this指针指向被调用的成员函数所属的对象
this指针隐含在每一个非静态成员函数
不需要定义,直接用即可
用途:
当形参和成员变量同名时,可以用this指针来区分
来类的非静态成员函数返回对象本身时,可以用 return *this
空指针访问成员函数时,如果成员函数没有用到成员变量,则可以访问
在成员函数后加const,我们称为常函数
长函数不可修改成员属性
成员属性声明时,加关键字mutable,则常函数中可以修改
常对象
如果在对象前加const 则成为常对象 常对象只能调用常函数
// this指针本质是指针常量 Person * const this
//在成员函数后加const 修饰的是this的指向,使其指向的值也不可以修改
//但是 在变量前加 mutable 则常函数也可以修改变量
void showPerson() const
{
this->age = 20;
}
int a;
mutable int age;//特殊变量 ,常函数可以修改
//常对象,其中的值不可以修改,但是mutable修饰的变量可以修改
const Person p1(10);
//常对象这只能调用常函数、
p1.showPerson();
### 友元
友元的目的:让一个函数或类访问另一个类中的私有成员
友元的三种实现:
1.全局函数做友元
2.类做友元
3.成员函数做友元
1.
一个全局函数访问私有成员时,需要在类的开头声明友元函数,
class Building
{
//友元函数
friend void GoodGay(Building* building);
}
2.友元类GoodGay可以访问私有成员:卧室
class Building
{
//友元类
friend class GoodGay;
public:
Building();
string m_sittingroom;//客厅
private:
string m_bedroom;//卧室
};
//类外写成员函数
Building::Building()
{
m_sittingroom = "客厅";
m_bedroom = "卧室";
}
3.成员函数做友元
在声明两个类时,如果一个类在前,但是其中有后面类的变量或内容,就需要前向声明
class Building;
class GoodGay
{
public:
Building * building;//如果没有前向声明就会报错
};
但是如果调换building和goodgay的顺序依然会报错
GoodGay 类需要 Building 的完整定义,而 Building 又需要 GoodGay 的完整定义
这是一个 循环依赖 问题,需要调整代码顺序来解决。
class GoodGay;
class Building
{
//编译器无法知道visit函数
//虽然你前向声明了 class GoodGay;,但前向声明只告诉编译器 GoodGay 是一个类,而不会提供其成员函数的声明。
friend void GoodGay::visit();
}
### 运算符重载
可以对 + - 等运算符进行 自定义数据类型 的运算
只可以重载 自定义的数据类型 但是内置数据类型不可以重载,如int double 等,运算符不可重载
成员函数重载运算符
Person operator+(Person &p)
全局函数重载运算符
Person operator+(Person &p1,Person &p2)
p3=p2+p1
也可以Person operator+(Person &p1,int a)
p3=p1+10
但是 成员函数的运算符重载 不可以写为,只有全局函数可以将非类的数据类型做为左值
p3=10+p2
这是因为成员函数的第一个参数是隐式的 this 指针,它指向调用该函数的对象(必须是类类型)。因此,成员函数重载的运算符无法将非类类型作为左操作数。
//成员函数重载左移运算符 p.operator<<(cout) 简化版本 p<<cout
//不会利用成员函数重载<<,因为无法实现 cout 在左侧
左运算符是ostream类型
ostream& operator<<(ostream & cout, Person& p)
{
cout << "m_a:" << p.m_a << "m_b:" << p.m_b << endl;
return cout;
}
如果访问有私有成员 则可以利用友元技术访问私有成员
friend ostream& operator<<(ostream & cout, Person& p);
//如果不用 & 符号,则在cout << ++(++myint) << endl;
// ++myint后,再次++的对象变为拷贝值,而不是myint本身,
//返回的是 & 后,就可以一直对一个数据进行操作
//前置运算
myinteger& operator++()
{
m_num++;
return *this;
}
//后置运算
//int 代表展位参数,可以用于区分前置和后置
//后置递增不需要 返回&
myinteger operator++(int)
{
//先记录当前结果
myinteger temp = *this;
//后递增
m_num++;
//返回记录结果
return temp;
}
赋值运算符重载用于在堆区开辟内存的变量的赋值 (将浅拷贝 变为 深拷贝)
如果类的变量开辟在堆区,然后再用赋值操作,P1=P2则P2的变量和P1的变量指向同一块堆区内存,在析构时就会对同一区域释放两次,造成程序崩溃
#### 函数调用运算符重载
函数调用运算符()也可以重载
由于重载后使用的方式非常像函数的调用,因此被称为仿函数
仿函数没有固定写法,非常灵活
多用于stl
void operator()(string test)
{
cout << test << endl;
}
p1("chenxi");//由于使用起来非常像函数,所以被称为仿函数
myadd a1;
int ret = a1(100, 100);
cout << ret << endl;
cout << myadd()(10, 10) << endl;
myadd() 匿名对象显示创建,
myadd()(10, 10) 仿函数重载
## 继承
继承的好处:减少重复代码
语法: class 子类 :继承方式 父类
子类 也称为 派生类
父类 也称为 基类
继承的继承方式:
公共继承
保护继承
私有继承
父类的private 子类无论以那种方式都访问不到
公共继承 父类内的 公共和保护的成员 权限不改变
保护继承 父类内的 公共和保护的成员 全变为保护权限
私有继承 父类内的所有成员 全变为子类的私有权限
#### 继承中的对象模型
父类中所有的非静态成员 无论什么权限,都会被子类继承下去,
私有成员被编译器因此,访问不到,但是确实被继承了
开发人员命令提示工具 developer...
c1 /dl reportSingClassLayout类名 文件名
可以查看对象模型
子类和父类继承中 构造和析构顺序
base 构造
son 构造
son 析构
base 析构
#### 继承中 同名成员处理方式
1、访问子类同名成员 直接访问
2、访问父类同名成员 需要加作用域
//如果子类和父类出现同名的成员函数,子类的同名成员函数会隐藏掉父类的
//通过子类访问父类的同名成员函数 需要加作用域
继承中 子类不要写和父类一样的成员变量
静态成员处理方式:
静态成员变量
//通过对象访问
son s1;
cout<< s1.m_b <<endl;
//通过子类访问父类的同名成员 需要加作用域
cout << s1.base::m_b << endl;
//通过类名访问
//son::表示 通过类名方式访问 base:: 表示访问base类的内容
cout << son::base::m_b << endl;
cout << son::m_b << endl;
静态成员函数:
s1.func3();
son::func3();
s1.base::func3();
son::base::func3();
#### 多继承语法
语法: class: 继承方式 父类1,继承方式 父类2,....
多继承也可能会引发同名问题,在同名时需要加作用域
如果base1 base2 都有m_a
使用时需要加作用域
cout<<s.baes1::m_a;
cout<<s.baes2::m_a;
在开发中不建议使用多继承
#### 菱形继承
两个派生类继承了同一个基类
又有一个类继承了两个派生类
这种继承叫做菱形继承 也叫钻石继承
//利用虚继承 可以解决菱形继承的问题
class sheep :virtual public animal
v b ptr 虚基类指针
virtual base pointer
虚基类指针会指向 vbtable
virtual base table
## 多态(继承中 子类不要写和父类一样的成员变量)
多态分为两类
静态多态:函数重载、运算符重载、复用函数名
动态多态:派生类和虚函数实现运行时多态
区别:
静态的函数地址早绑定,编译阶段确定函数地址
动态的函数地址晚绑定,运行阶段确定函数地址
virtual void speak() 虚函数可以实现晚绑定
动态多态满足条件:
1、有继承关系
2、子类重写父类中的虚函数
类中只有一个虚函数时,类会有一个占4字节大小的指针 vfptr 虚函数(表 )指针
virtual function pointer
指向虚函数表 vftable 该表记录虚函数地址
子类会继承这个虚函数指针,
当子类重写了这个虚函数, 子类中的虚函数表会替换成 子类的虚函数地址
当父类的指针 或引用 指向子类对象时, 发生多态
animal &animal =cat
虚函数会去找传入类的虚函数表
多态好处:
组织结构清晰 出错可以定位确定的代码段
可读性强
对前期和后期的扩展和维护性高
### 纯虚函数 抽象类
通常父类的虚函数的实现时毫无意义的,称为纯虚函数
当类中有了纯虚函数 就成为抽象类
抽象类的特点:
无法实例化函数
子类必须重写抽象类的纯虚函数,否则也属于抽象类
### 虚析构和纯虚析构
多态时,子类如果有属性开辟到堆区,父类释放时无法调用到子类的虚构代码
解决方式就是析构改为虚析构或纯虚析构
//父类指针在析构时,不会调用子类的析构函数
//子类如果有堆区的属性,会造成内存泄露
virtual~animal()
{
cout << "animal xi gou " << endl;
}
父类为虚析构时,就可以解决释放不干净的问题
class animal
{
------
virtual~animal() = 0;
};
animal::~animal()
{
cout << "animal chun xu xi gou " << endl;
}
纯虚析构需要有声明 也需要具体实现
有了纯虚析构 也属于抽象类
## 文件操作
#include<fstream>
//1.包含 ftream头文件
//2.创建流对象
ofstream ofs;
//3.指定打开方式
ofs.open("test.txt",ios::out);
//4.写内容
ofs << "chenxi" << endl;
//5.关闭文件
ofs.close();
文件打开方式:
ios::in 为读文件而打开文件
ios::out 为写文件而打开文件
ios::ate 初始位置:文件尾
ios::app 追加方式写文件
ios::turnc 如果文件存在先删除,在创建
ios::binary 二进制方式
case 语句过长 需要把下面包括break的语句括起来
# 模板 泛型编程 STL
## 函数模板
模板调用规则:
如果函数模板和普通函数都可以实现,优先调用普通函数
可以通过空模板参数列表来强制调用函数模板
函数模板也可以发生重载
函数模板可以产生更好的匹配,优先调用函数模板
通过模板重载,可以为具体类型提供具体化的模板
## 类模板
类模板没有自动类型推导的使用方式,
类模板参数列表可以有默认参数
类模板在创建成员时,可以调用其他类的函数,
然后
在创建具体的类时,
该类有哪个成员函数才能调用哪一个
成员函数的调用时机:
类模板中的成员函数并不是一开始就创建的,在 调用时 才会确定是哪个具体的类 才会去创建