目录

1 内存分区模型

程序运行前

01 代码区

02 全局区

程序运行后

03 栈区

04 堆区

05 new操作符

2 引用

06 引用的基本使用

07 引用注意事项

08 引用做函数参数

09 引用做函数返回值

10 引用的本质

11 常量引用

3 函数提高

12函数默认参数

13 函数占位参数

14 函数重载

4 类和对象

封装

16 封装的意义 属性和行为作为整体

对象特性:对象的初始化和清理

23 构造函数和析构函数

24 构造函数的分类和调用

25 拷贝构造函数调用时机

26 构造函数调用规则

27 深拷贝和浅拷贝

28 初始化列表

29 类对象作为类成员

30 静态成员

对象特性:C++对象模型和this指针

32 成员变量和成员函数分开存储

33 this指针

34 空指针访问成员函数

35 const修饰成员函数

友元

36 全局函数做友元

37 友元类

38 成员函数做友元

C++运算符重载

39 加号运算符重载

40 左移运算符重载

41 递增运算符重载

42 赋值运算符重载

43 关系运算符重载

44 函数调用运算符重载

继承

45 基本语法

46 继承方式

47 继承中的对象模型

48 构造和析构顺序

49 同名成员处理

50 同名静态成员处理

51 多继承语法

52 菱形继承问题以及解决方法

多态

53 多态的基本语法

54 多态的原理剖析

56 纯虚函数和抽象函数

58 虚析构和纯虚析构

5 文件操作

文本文件

143 写文件

144 读文件

二进制文件

145 写文件

146 读文件


1 内存分区模型

程序运行前

程序运行运行前,编译后:

01 代码区

代码区:存放函数的二进制代码,有操作系统进行管理
共享,只读

02 全局区

全局区:存放全局变量静态变量(static),常量(字符串常量const修饰的全局常量(全局常量))
【在程序结束后由操作系统释放】
//不在全局区的数据:局部变量、const修饰的局部变量(局部常量)

程序运行后

03 栈区

栈区:由编译器自动分配释放,存放函数的形参,局部变量
//不要返回局部变量的地址,栈区开辟的数据由编译器自动释放

04 堆区

堆区:由程序员分配和释放,若程序员不释放,系统结束时由操作系统回收
//new可以创建堆区数据,返回的是创建的堆区的地址,要用指针接收

05 new操作符

int* arr = new int[10];//堆区开辟数组

delete[] arr;//释放数组

2 引用

06 引用的基本使用

作用:给变量起别名
语法:数据类型 &别名=原名;

07 引用注意事项

引用必须初始化
引用初始化后不可以改变

 int a = 10;//int& b;//err,引用必须初始化int& b = a;int c = 20;b = c;//这是赋值操作,不是更改引用,把b的值赋值成c的值20cout << a << b << c << endl;//20 20 20

08 引用做函数参数

引用传递让形参修饰实参,简化指针修改实参

通过引用参数产生的效果同按地址传递是一样的,引用的语法更清楚简单

//引用传递
void mySwap(int &a,int &b)
{int temp = a;a = b;b = temp;
}int main()
{int a = 1;int b = 2;cout << "a=" << a << endl;//1cout << "b=" << b << endl;//2mySwap(a, b);cout << "swap a=" << a << endl;//2cout << "swap b=" << b << endl;//1system("pause");return 0;
}

09 引用做函数返回值

作用:引用是可以作为函数的返回值存在的

注意:不要返回局部变量引用

用法:函数返回值是引用时,函数调用可以作为左值

//返回局部变量引用,非法操作
int& test01()
{int a = 10;//局部变量存放在栈区return a;
}//返回静态变量引用,可以执行
int& test02()
{static int a = 10;//静态变量,存放在全局区,系统释放return a;
}int main()
{//1.不要返回局部变量的引用int& ref = test01();cout << ref << endl;//第一次正确,因为编译器做了保留//cout << ref << endl;//第二次错误,因为a的内存已经释放int& ref2 = test02();cout << ref2 << endl;//10cout << ref2 << endl;//10cout << ref2 << endl;//10//2.如果函数的返回值是引用,这个函数的调用可以作为左值进行赋值操作test02() = 1000;cout << ref2 << endl;//1000cout << ref2 << endl;//1000system("pause");return 0;
}

10 引用的本质

引用的本质在C++内部实现是一个指针常量

(指针的指向是不可以修改,指针指向的值是可以改动

11 常量引用

用来修饰形参,防止误操作

(在函数形参列表中,可以加const修饰形参,防止形参改变实参)

void showValue(const int& val)//函数中利用常量引用防止误操作修改实参
{cout << val;
}
int main()
{//常量引用:用来修饰形参,防止误操作int a = 10;//int& ref = 10;//err,引用必须引一块合法的内存空间const int& ref = 10;//编译器帮修改为int temp=10;int &ref=temp;//ref = 20;//加入const之后,变为只读,不可修改int a = 100;showValue(a);system("pause");return 0;
}

3 函数提高

12函数默认参数

语法:返回值类型 函数名(形参=默认值){ }

  • C++中函数的形参列表中的形参是可以有默认值的
  • 如果有传入数据,就用传入的数据,如果没有就用默认值
  • 如果某个位置已经有了默认参数,那么这个位置之后,从左到右都必须有默认值
  • 声明和实现只能有一个有默认参数;如果函数声明有默认参数,函数实现就不能有默认参数

13 函数占位参数

C++中函数形参列表里可以有占位参数,用来占位,调用函数时必须填补该位置

语法:返回值类型 函数名(数据类型){}
    //占位参数还可以有默认参数 (int=10),有默认值时,调用时可以不用传值

14 函数重载

函数名可以相同,提高复用性

满足条件:

  1. 同一个作用域;
  2. 函数名称相同;
  3. 函数参数类型不同或者个数不同或者顺序不同

函数的返回值不可以作为函数重载的条件

注意事项:

  1. 引用可以作为函数重载条件,const int &a和int &a(const可以作为引用重载条件)
  2. 函数重载碰到默认参数,容易出现二义性,尽量避免

4 类和对象

面向对象三大特性:封装、继承、多态
万物是对象,有属性和行为

具有相同性质的对象,可以抽象为类

封装

16 封装的意义 属性和行为作为整体

//设计一个圆类,求周长

const double PI = 3.14;class Circle
{//访问权限:公共权限
public ://属性int m_r;//半径//行为:获取圆的周长double calculateZC(){return 2 * PI * m_r;}
};//实例化(通过一个类,创建一个对象的过程)Circle c1;//通过圆类,创建具体的圆(对象)c1.m_r = 10;//给圆的对象的属性赋值cout << "圆的周长为:" << c1.calculateZC() << endl;

实例化:通过一个类,创建一个对象的过程

类中的属性和行为,统称为成员

  • 属性  成员属性 成员变量
  • 行为 成员函数 成员方法

类在设计时,可以把属性和行为放在不同的权限下,加以控制

18 三种访问权限
//公共权限 public          成员类内可以访问,类外可以访问
//保护权限 protected    类内可以访问,类外不可以访问            儿子可以访问父亲中的保护内容
//私有权限 private        类内可以访问,类外不可以访问            儿子不可以访问父亲中的私有内容

19 struct和class的区别:

默认的访问权限不同 
struct默认权限是  公共  public
class默认权限是   私有  private

20 成员属性设置为私有的优点

  1. 可以自己控制读写权限
  2. 对于写权限,可以检测数据的有效性

//设计立方体类

//创建立方体类
class Cube {
public:void setL(int l){m_L = l;}void setW(int w){m_W = w;}void setH(int h){m_H = h;}int getL(){return m_L ;}int getW(){return m_W;}int getH(){return m_H;}int calculateS(){return 2 * m_L * m_W + 2 * m_H * m_W + 2 * m_L * m_H;}int calculateV(){return m_L * m_W * m_H;}//利用局部函数判断是否相等bool isSamebyclass(Cube& c){if (getL() == c.getL() && getW() == c.getW() && getH() == c.getH())return true;elsereturn false;}
private:int m_L;int m_W;int m_H;
};//利用全局函数判断两个立方体是否相等
bool isSame(Cube& c1, Cube& c2)
{if (c1.getL() == c2.getL() && c1.getW() == c2.getW() && c1.getH() == c2.getH())return true;elsereturn false;
}int main()
{Cube c1;c1.setL(10);c1.setW(10);c1.setH(10);cout<<"面积为"<<c1.calculateS()<<endl;cout << "体积为" << c1.calculateV() << endl;Cube c2;c2.setL(11);c2.setW(10);c2.setH(10);cout << "面积为" << c2.calculateS() << endl;cout << "体积为" << c2.calculateV() << endl;//利用全局函数判断bool ret=isSame(c1,c2);if (ret)cout << "相等" << endl;elsecout << "不相等" <<endl;//利用局部函数判断bool ret1 = c1.isSamebyclass(c2);if (ret1)cout << "相等" << endl;elsecout << "不相等" << endl;system("pause");return 0;
}

//判断点和圆的关系

在类中可以让另一个类 作为本类中的成员

可以把一个类拆在不同文件中 .h声明 .cpp实现

对象特性:对象的初始化和清理

每个对象也会有初识设置以及对象销毁前的清理数据的设置

23 构造函数和析构函数

编译器自动调用,完成对象初始化和清理工作

  • 构造函数:创建对象时对象的成员属性赋值
  • 析构函数:对象销毁前,清理工作

构造函数:类名(){}

  1. 没有返回值,不用写void
  2. 函数名与类名相同
  3. 构造函数可以有参数,可以发生重载
  4. 创建对象的时候,构造函数会自动调用,而且只调用一次

析构函数:~类名(){}

  1. 没有返回值,不用写void
  2. 函数名与类名相同
  3. 构造函数不可以有参数,不可以发生重载
  4. 创建对象的时候,构造函数会自动调用,而且只调用一次

24 构造函数的分类和调用

构造函数分类:
按照参数:有参构造和无参构造(默认构造)
按照类型:普通构造和拷贝构造

调用方式:
1.括号法2.显示法3.隐式转换法

class Person
{
public://无参构造Person(){cout << "Person的无参(默认)构造函数调用" << endl;}//有参构造Person(int a){age = a;cout << "Person的有参构造函数调用" << endl;}//拷贝构造函数Person(const Person &p)//const+引用方式传入{//将传入的人身上的所有属性,拷贝到当前对象身上age = p.age;cout << "Person的拷贝构造函数调用" << endl;}~Person(){cout << "Person的析构函数调用" << endl;}int age;
};void test01()
{//1.括号法Person p;//默认构造函数的调用,调用默认构造函数的时候,不要加()Person p1(10);Person p2(p1);//拷贝构造函数调用cout << "p2的年龄为" << p1.age << endl;cout << "p2的年龄为" << p2.age << endl;//注意:调用默认构造函数的时候,不要加()//Person p1();//err//编译器会认为是一个函数的声明,不会认为在创建对象2.显示法Person p1;Person p2 = Person(10);Person p3 = Person(p2);//Person(10);//匿名对象,特点:当前行执行结束后,系统会立即回收掉匿名对象注意:不要利用拷贝构造函数 初始化匿名对象Person(p3);编译器会认为在对象声明==Person p3;3.隐式转换法Person p4 = 10;//相当于 Person p4=Person(10);Person p5 = p4;
}int main()
{test01();system("pause");return 0;
}

25 拷贝构造函数调用时机

拷贝构造函数调用时机三种情况:

  1. 使用一个已经创建完毕的对象来初始化一个对象
  2. 值传递的方式给函数参数传值
  3. 以值方式返回局部对象
class Person
{
public:Person(){cout << "默认构造函数的调用" << endl;}Person(int age){m_Age = age;cout << "有参构造函数的调用" << endl;}Person(const Person &p){m_Age = p.m_Age;cout << "拷贝构造函数的调用" << endl;}~Person(){cout << "析构函数的调用" << endl;}int m_Age;
};//1.使用一个已经创建完毕的对象来初始化一个对象void test01()
{Person p1(20);Person p2(p1);cout << "p2的年龄为:" << p2.m_Age << endl;
}//2.值传递的方式给函数参数传值void doWork(Person p)
{}
void test02()
{Person p;doWork(p);
}
//3.以值方式返回局部对象Person doWork2()
{Person p1;return p1;
}
void test03()
{Person p = doWork2();
}
int main()
{//test01();//test02();test03();system("pause");return 0;
}

26 构造函数调用规则

调用规则:

如果我们写了有参构造函数,编译器就不再提供默认构造,依然提供拷贝构造
如果我们写了拷贝构造函数,编译器就不再提供其他普通构造函数

27 深拷贝和浅拷贝

浅拷贝:简单的复制拷贝操作
浅拷贝带来的问题就是堆区的内存重复释放

深拷贝:在堆区重新申请空间,进行拷贝操作

//如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题

class Person
{
public:Person(){cout << "Person的默认构造函数调用" << endl;}Person(int age,int height){m_Age = age;m_Height=new int(height);cout << "Person的有参构造函数调用" << endl;}//自己实现拷贝构造函数,解决浅拷贝带来的问题Person(const Person& p){cout << "Person拷贝构造函数调用" << endl;m_Age = p.m_Age;//m_Height = p.m_Height;编译器默认实现的代码,浅拷贝//深拷贝操作m_Height = new int(*p.m_Height);}~Person(){//析构代码,将堆区开辟数据做释放操作if (m_Height != NULL){delete m_Height;m_Height = NULL;}cout << "Person的析构函数调用" << endl;}int m_Age;//年龄int* m_Height;
};void test01()
{Person p1(18,160);cout << "p1的年龄为:" << p1.m_Age <<"身高为:"<<*p1.m_Height<< endl;Person p2(p1);cout << "p2的年龄为:" << p2.m_Age << "身高为:" << *p2.m_Height << endl;}
int main()
{test01();system("pause");return 0;
}

28 初始化列表

 //初始化列表Person(int a,int b,int c) :m_A(a), m_B(b), m_C(c){}
//属性赋初值
Person p(31,20,10);

29 类对象作为类成员

类中的成员可以是另一个类的对象,我们称该成员为对象成员

  • 当其他类对象作为本类成员,构造时候先构造类对象,再构造自身
  • 当其他类对象作为本类成员,析构时候先析构自身,再析构类对象

构造顺序与析构顺序相反

class Phone
{
public:Phone(string pName){m_PName = pName;cout << "phone的构造函数调用" << endl;}~Phone(){cout << "phone的析构函数调用" << endl;}string m_PName;
};
class Person
{
public:Person(string name, string pName):m_Name(name),m_phone(pName){cout << "person的构造函数调用" << endl;}~Person(){cout << "person的析构函数调用" << endl;}string m_Name;Phone m_phone;
};

30 静态成员

静态成员变量:

  • 所有对象共享同一份数据
  • 在编译阶段分配内存(程序运行前-全局区)
  • 类内声明,类外初始化
class Person
{
public:static int m_A;//类内声明};int Person::m_A = 100;//类外初始化void test01()
{Person p;cout << p.m_A << endl;//100Person p2;p2.m_A = 200;//所有对象共享同一份数据cout << p.m_A << endl;//200
}

静态成员变量,不属于某个对象上,所有对象都共享同一份数据 
有两种访问方式:

  1. 通过对象进行访问
  2. 通过类名进行访问
Person p;
cout<<p.m_A<<endl;cout<<Person::m_A<<endl;

静态成员变量有访问权限,类外访问不到私有静态成员变量

31 静态成员函数

  • 所有对象共享同一个函数
  • 静态成员函数只能访问静态成员变量

//两种访问方式:通过对象,通过类名
//静态成员函数也是有访问权限的,类外访问不到私有的静态成员函数

class Person
{
public://静态成员函数static void func(){m_A = 100;//静态成员函数可以访问静态成员变量,共享的//m_B = 200;//静态成员函数不可以访问非静态成员变量,因为无法区分到底是哪个对象的m_Bcout << "ststic void func()的调用" << endl;}static int m_A;//类内声明,类外初始化int m_B;//非静态成员变量
};int Person::m_A=0;//类外初始化void test01()
{//静态成员函数有两种访问方式//通过对象访问Person p;p.func();//通过类名访问,因为不属于某一个对象上,不创建对象也可以访问Person::func();
}

对象特性:C++对象模型和this指针

32 成员变量和成员函数分开存储

只有非静态成员变量 属于对象上的数据

空对象占用内存空间为:1
    //C++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置
    //每个空对象也应该有一个独一无二的内存地址

class Person
{int m_A;//非静态成员变量 属于对象上的数据static int m_B;//静态成员变量  不属于类的对象上void func(){}//非静态成员函数 不属于类的对象上static void func2() {}//静态成员函数 不属于类的对象上
};
int Person::m_B=0;void test02()
{Person p;cout << "sizeofp=" << sizeof(p) << endl;//4
}

33 this指针

this指针:指向被调用的成员函数所属的对象
this指针是隐含每一个非静态成员函数内的一种指针
this指针不需要定义,直接使用
用途:
//当形参和成员变量同名时,可用this指针来区分
//在类的非静态成员函数中返回对象本身,可使用return *this

class Person
{
public:Person(int age){//this指针指向被调用的成员函数所属的对象this->age = age;}//如果用Person返回就是用值的方式返回,会用到拷贝构造函数,Person返回的和自身是不一样的数据//用引用方式返回,不会创建新的对象,会一直返回P2本体Person& PersonAddAge(Person& p)//Person& 不返回引用就会通过拷贝构造创建新对象,就不是原来的对象了{this->age += p.age;//this是指向p2的指针,*this指向的就是p2这个对象的本体return *this;}int age;
};
void test02()
{Person p1(10);Person p2(10);p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);//链式编程思想cout << "p2的年龄为:" << p2.age << endl;
}
void test01()
{Person p1(18);cout << "p1的年龄为:" << p1.age << endl;
}test01();test02();

34 空指针访问成员函数

C++中允许空指针调用成员函数,也要注意有没有用到this指针
//如果用到this指针,需要判断保证代码的健壮性

35 const修饰成员函数

常函数
成员函数后加const称为常函数
//常函数内不可以修改成员属性(指针指向的值不能修改)
//成员属性声明时加关键字mutable后,在常函数中依然可以修改
常对象
声明对象前加const称为常对象
//常对象只能调用常函数

友元

36 全局函数做友元

class Building
{friend void goodgay(Building* builiding);//goodgay是Building好朋友,可以访问Builing中私有成员
public:Building(){m_SittingRoom = "客厅";m_BedRoom = "卧室";}
public:string m_SittingRoom;private:string m_BedRoom;};
//全局函数
void goodgay(Building *builiding)
{cout << "全局函数正在访问:" << builiding->m_BedRoom << endl;cout << "全局函数正在访问:" << builiding->m_SittingRoom << endl;
}

37 友元类

friend class goodgay;//goodgay类是本类的好朋友,可以访问本类中私有的成员

38 成员函数做友元

friend void Goodgay::visit01();//goodgay类下的visit成员函数作为本类的好朋友,可以访问私有成员

C++运算符重载

运算符重载:对已有运算符重新进行定义,赋予另一种功能,以适应不同的数据类型

39 加号运算符重载

作用:实现两个自定义数据类型相加的运算
//对于内置的数据类型表达式运算符是不可能改变的
//不要滥用运算符重载

class Person
{
public://1.成员函数重载加号:   Person p3=p1+p2;成员函数的本质是 Person p3=p1.operator+(p2);/*Person operator+(Person& p){Person temp;temp.m_A = this->m_A + p.m_A;temp.m_B = this->m_B + p.m_B;return temp;}*/int m_A;int m_B;};//2.全局函数重载加号: Person p3=p1+p2;全局函数的本质是 Person p3= operator+(p1,p2);
Person operator+(Person &p1,Person &p2)
{Person temp;temp.m_A = p1.m_A + p2.m_A;temp.m_B = p1.m_B + p2.m_B;return temp;
}//3.运算符重载也可以发生函数重载
Person operator+(Person& p1, int num)
{Person temp;temp.m_A = p1.m_A +num;temp.m_B = p1.m_B +num;return temp;
}
void test01()
{Person p1;p1.m_A = 10;p1.m_B = 10;Person p2;p2.m_A = 10;p2.m_B = 10;Person p3 = p1 + p2;//运算符重载也可以发生函数重载Person p4 = p1 + 100;cout << "p3.m_A=" << p3.m_A << endl;cout << "p3.m_B=" << p3.m_B << endl;cout << "p4.m_A=" << p4.m_A << endl;cout << "p4.m_B=" << p4.m_B << endl;
}

40 左移运算符重载

输出左移运算符配合友元可以实现输出自定义数据类型

class Person
{friend ostream& operator<<(ostream& cout, Person& p);
public:Person(int a, int b){m_A = a;m_B = b;}
private://不会利用成员函数重载左移运算符<<,因为无法实现cout在左侧int m_A;int m_B;};//只能利用全局函数重载左移运算符
ostream& operator<<(ostream &cout,Person &p)//本质是operator<<(cout,p)  简化cout<<p
{cout << "m_A=" << p.m_A << endl;cout << "m_B=" << p.m_B << endl;return cout;//为了可以无限往后追加输出
}
void test01()
{Person p(10, 10);cout << p <<"hello"<< endl;;
}

41 递增运算符重载

通过重载递增运算符,实现自己的整形数据

前置递增返回参数,后置递增返回值

//++//自定义整形
class MyInteger
{friend ostream& operator<<(ostream& cout, MyInteger myint);
public:MyInteger(){m_Num = 0;}//重载++运算符//1.重载前置++ MyInteger& operator++()//返回引用是为了一直对一个数据进行递增操作{//先进行++运算m_Num++;//再将自身返回return *this;}//2.重载后置++ //返回的是值,因为返回的是局部变量MyInteger operator++(int)//int代表占位参数,可以用于区分前置和后置递增,编译器会认为这是后置递增重载{//先记录当时结果MyInteger temp = *this;//后递增m_Num++;//再返回之前记录的结果return temp;}
private:int m_Num;
};//重载左移运算符
ostream& operator<<(ostream& cout, MyInteger myint)
{cout << myint.m_Num << endl;return cout;
}void test01()
{MyInteger myint;cout << ++(++myint) << endl;
}
void test02()
{MyInteger myint;cout << myint++ << endl;cout << myint << endl;
}test01();test02();

42 赋值运算符重载

 Person& operator=(Person& p){//编译器提供的是浅拷贝//m_Age=p.m_Age;//应该先判断有属性在堆区,如果有先释放干净,然后再深拷贝if (m_Age != NULL){delete m_Age;m_Age = NULL;}m_Age=new int(*p.m_Age);//深拷贝return *this;}

43 关系运算符重载

//重载==bool operator==(Person& p){if (this->m_Age == p.m_Age && this->m_Name == p.m_Name){return true;}else false;}//重载!=bool operator!=(Person& p){if (this->m_Age == p.m_Age && this->m_Name == p.m_Name){return false;}else true;}

44 函数调用运算符重载

函数调用运算符()也可以重载

由于重载后使用的方式非常像函数的调用,因此称为仿函数

仿函数没有固定的写法,非常灵活

//打印输出
class MyPrint
{
public://重载函数调用运算符void operator()(string test){cout << test << endl;}
};
void test01()
{MyPrint myPrint;myPrint("hello");//由于使用起来类似于函数调用,称为仿函数
}
//仿函数非常灵活,没有固定的写法
//加法类
class MyAdd
{
public:int operator()(int num1, int num2){return num1 + num2;}
};
void test02()
{MyAdd myadd;int ret = myadd(100, 200);cout << ret << endl;//匿名函数对象 //MyAdd()cout << MyAdd()(100, 200) << endl;
}
int main()
{test01();test02();return 0;
}

继承

45 基本语法

继承的好处:减少重复代码
语法: class 子类:继承方式 父类

class A : publib B
            A类,子类也称为派生类
            B类,父类也称为基类

派生类中的成员,包含两大部分:一类是从基类继承过来的,一类是自己增加的成员

从基类继承过来的表现其共性,而新增的成员体现其个性

46 继承方式

继承的三种方式:

  • 公共继承
  • 保护继承
  • 私有继承

47 继承中的对象模型

class Base
{
public:int a;
protected:int b;
private:int c;};class Son :public Base
{
public:int d;};void test01()
{//父类中所有静态成员属性都会被子类继承下去//父类中私有成员属性是被编译器给隐藏了,因此是访问不到,但是确实被继承下去了cout << "size of Son=" << sizeof(Son) << endl;//16
}

利用开发人员命令提示工具(Developer Command Prompt for VS 2019)查看对象模型
//跳转盘符 F:
//跳转文件路径 cd 具体路径下
//查看命令 cl /d1 reportSingleClassLayout类名 文件名
                F:\C++-code\0421继承>cl /d1 reportSingleClassLayoutSon "129对象基本模型.cpp" 

48 构造和析构顺序

子类继承父类后,当创建子类对象,也会调用父类的构造函数
继承中的构造和析构的顺序:
先构造父类,再构造子类;析构顺序与构造顺序相反

49 同名成员处理

当子类与父类出现同名的成员,如何通过子类对象,访问到其中的同名数据:
1.访问子类同名成员,直接访问即可
2.访问父类同名成员,需要加作用域

 Son s;cout << "Son的m_A=" << s.m_A << endl;cout << "Base的m_A=" << s.Base::m_A << endl;//如果通过子类对象,访问到父类中的同名成员,需要加作用域s.func();//直接调用的是子类中的同名函数s.Base::func();//调用的是父类的
  • 如果子类中出现和父类中同名的成员函数,子类的同名成员会隐藏掉父类中所有同名成员函数(包括重载版本)
  • 如果想访问到父类中被隐藏的同名成员函数,需要加作用域 

50 同名静态成员处理

同名静态成员处理方式和非静态处理方式一样,只不过有两种访问方式(通过对象和通过类名)

 class Base{public:static int m_A;static void func(){cout << "Base 的func的调用" << endl;}};int Base::m_A = 100;class Son :public Base{public:static int  m_A;static void func(){cout << "Son 的func的调用" << endl;}};int Son::m_A = 200;void test01(){//通过对象访问Son s;cout << "Son的m_A=" << s.m_A << endl;cout << "Base的m_A=" << s.Base::m_A << endl;//通过类名访问cout << Son::m_A << endl;cout << Base::m_A << endl;cout << Son::Base::m_A << endl;//第一个::代表通过类名方式访问,第二个::代表访问父类作用域下的m_A}void test02(){//通过对象访问Son s;s.func();s.Base::func();//通过类名访问Son::func();Son::Base::func();}

51 多继承语法

C++允许一个类继承多个类

语法:class 子类:继承方式 父类1,继承方式 父类2
//多继承可能会引发父类中有同名成员出现,需要加作用域
//开发中不建议用多继承

//子类继承两个父类class Son :public Base1, public Base2
//父类中出现同名成员时,需要加作用域区分cout << s.Base1::m_A << endl;cout << s.Base2::m_A << endl;

52 菱形继承问题以及解决方法

菱形继承、钻石继承

  • 两个派生类继承同一个类
  • 又有某个类同时继承两个派生类

多态

53 多态的基本语法

动态多态满足条件:

  1. 要有继承关系
  2. 子类重写父类的虚函数   //重写:函数返回值类型 函数名 参数列表 完全相同

动态多态使用:父类的指针或引用 指向子类对象 

54 多态的原理剖析

//示例:多态原理剖析

//动物类
class Animal
{
public:virtual void speak(){cout << "动物在说话" << endl;}
};//猫类
class Cat :public Animal
{
public://虚函数void speak(){cout << "小猫在说话" << endl;}
};//狗类
class Dog :public Animal
{
public://虚函数void speak(){cout << "小狗在说话" << endl;}
};
void doSpeak(Animal &animal)
{animal.speak();
}void test01()
{Cat cat;doSpeak(cat);Dog dog;doSpeak(dog);}

多态优点:代码组织结构清晰,可读性强,利于前期和后期的扩展及维护

在真实的开发中,提倡 开闭原则
开闭原则:对扩展进行开发,对修改进行关闭

56 纯虚函数和抽象函数

当类中有了纯虚函数,这个类就是抽象类 

//抽象类无法实例化对象
 //子类必须重写抽象类中的纯虚函数,否则也属于抽象类,无法实例化对象

58 虚析构和纯虚析构

父类指针在析构的时候,不会调用子类中析构函数,导致子类中如果有堆区属性,出现内存泄漏

虚析构和纯虚析构的共性:

  • 可以解决父类指针释放子类对象时不干净的问题
  • 都需要有具体的函数实现

虚析构和纯虚析构的区别:

  • 如果是纯虚析构,该类属于抽象类,无法实例化对象

虚析构:virtual ~类名(){}

纯虚析构virtual ~类名() = 0;//声明
                  类名 :: ~类名() {}  //具体实现

总结:

  1. 虚析构和纯虚析构就是用来解决通过父类指针释放子类对象
  2. 如果子类中没有堆区数据,可以不写虚析构或纯虚析构
  3. 拥有纯虚析构函数的类也属于抽象类

5 文件操作

文本文件

143 写文件

写文件步骤:

  1. 包含头文件#include<fstream>
  2. 创建流对象 ofstream ofs;
  3. 打开文件 ofs.open("文件路径",打开方式);
  4. 写数据 ofs << "写入的数据";
  5. 关闭文件 ofs.close();

ofstream ofs;
ofs.open("text.txt", ios::out);
ofs << "姓名:张三" << endl;
ofs.close();

144 读文件

ifstream ifs;
ifs.open("text.txt", ios::in);
if (!ifs.is_open())
{cout << "文件打开失败" << endl;return;
}
//读文件的四种方式
//1
char buf[1024] = { 0 };
while (ifs >> buf)
{cout << buf << endl;
}//2
char buf[1024] = { 0 };
while (ifs.getline(buf, sizeof(buf)))
{cout << buf << endl;
}//3
string buf;
while (getline(ifs, buf))
{cout << buf << endl;
}*//4不推荐
char c;
while ((c = ifs.get()) != EOF)
{cout << c;
}

二进制文件

以二进制的方式对文件进行读写操作

打开方式要指定为ios::binary

145 写文件

ofstream ofs;
ofs.open("Person.txt", ios::out | ios::binary);
Person p = { "张三",18 };
ofs.write((const char *)&p,sizeof(Person));
ofs.close();

146 读文件

 ifstream ifs;ifs.open("person.txt", ios::in | ios::binary);if (!ifs.is_open()){cout << "文件打开失败" << endl;return;}Person p;ifs.read((char*)&p, sizeof(Person));cout << p.m_name << p.m_age << endl;ifs.close();

笔记 黑马程序员C++教程从0到1入门编程——核心编程相关推荐

  1. 笔记 黑马程序员C++教程从0到1入门编程——基础语法入门

    目录 1.C++初识 02 C++书写helloworld 03 注释 04 变量 05 常量 06 关键字 07 标识符命名规则 2.数据类型 08 整型 09 sizeof关键字 10 实型(浮点 ...

  2. 笔记 黑马程序员C++教程从0到1入门编程——提高编程02

    目录 3 STL-常用容器 string容器 189 构造函数 190 赋值操作 191 字符串拼接 192 字符串查找和替换 193 字符串比较 194 字符存取 195 字符串插入和删除 196 ...

  3. 笔记 黑马程序员C++教程从0到1入门编程——提高编程03

    目录 4 STL-函数对象 237 函数对象 谓词 238 一元谓词 239 二元谓词 内建函数对象 240 算术仿函数 241 关系仿函数 242 逻辑仿函数 5 STL-常用算法 遍历算法 243 ...

  4. 笔记 黑马程序员C++教程从0到1入门编程——提高编程01

    目录 1 模板 01 模板的概念 02 函数模板基本语法 03 函数模板注意事项 04 函数模板案例-数组排序 05 普通函数与函数模板区别 06 普通函数与函数模板调用规则 07 模板的局限性 08 ...

  5. 【黑马程序员 C++教程从0到1入门编程】【笔记4】C++核心编程(类和对象——封装、权限、对象的初始化和清理、构造函数、析构函数、深拷贝、浅拷贝、初始化列表、友元friend、运算符重载)

    黑马程序员C++教程 文章目录 4 类和对象(类属性[成员属性],类函数[成员函数]) 4.1 封装 4.1.1 封装的意义(三种权限:public公共.protected保护.private私有)( ...

  6. 【黑马程序员 C++教程从0到1入门编程】【笔记3】C++核心编程(内存分区模型、引用、函数提高)

    黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难 文章目录 1 内存分区模型 1.1 程序运行前 1.2 程序运行后(手动开辟内存:c语言malloc,c++new) 1.3 new操作 ...

  7. 【黑马程序员 C++教程从0到1入门编程】【笔记2】通讯录管理系统

    黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难 文章目录 1.系统需求 2.创建项目 2.1 创建项目 3.菜单功能 4.退出功能 5.添加联系人 5.1 设计联系人结构体 5.2 设 ...

  8. 【黑马程序员 C++教程从0到1入门编程】【笔记1】数据类型、运算符、程序流程结构、数组、函数、指针、结构体

    黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难 文章目录 1.C++初识 1.1 第一个c++程序 1.2 注释 1.3 变量 1.4 常量 1.5 关键字 1.6 标识符命名规则 2 ...

  9. 【C++】黑马程序员 | c++教程从0到1入门编程笔记 | c++提高编程

    配套视频:https://www.bilibili.com/video/BV1et411b73Z 文章目录: 一.C++核心编程 二.C++提高编程 1 模板 本阶段主要针对C++泛型编程和STL技术 ...

  10. 【黑马程序员 C++教程从0到1入门编程】【笔记6】C++核心编程(文件操作)

    文章目录 5 文件操作 5.1文本文件 5.1.1写文件 5.1.2读文件 5.2 二进制文件 5.2.1 写文件(用二进制方式写时最好别用c++的string) 5.2.2 读文件 https:// ...

最新文章

  1. NeHe OpenGL第二十八课:贝塞尔曲面
  2. [Asp.net]绝对路径和相对路径
  3. openssl 创建私有CA
  4. java学习笔记-set
  5. 学习能力和工作态度是准绳
  6. editor.md国产markdown编辑器使用实例
  7. LeetCode题解-23 合并K个排序链表 Hard
  8. Visual Studio最常用、最高效的快捷键
  9. jupyter notebook 修改默认文件夹
  10. 遥感影像数据下载网址
  11. Android开发工程师已难找工作
  12. DDoS和CC攻击的技术原理区别
  13. 前端获取本地ip和外网ip
  14. 如何从根本上防止服务器被攻击
  15. 新品发布会直播推广的优势
  16. 使用docker在Centos上做DNS服务器的配置
  17. (ACWing217)绿豆蛙的归宿(数学期望)
  18. 苏州大学应用技术学院计算机二级,苏州大学应用技术学院怎么样_苏州大学一本与二本有什么差别...
  19. IoT物联网平台-Doc1
  20. 如何使用 Python 开发一个【抖音视频下载神器】

热门文章

  1. 淘宝客推广思维模式(转载)
  2. linux系统 锐捷_Ubuntu Linux锐捷安装方法
  3. 基于STM32设计的掌上游戏机(运行NES游戏模拟器)详细开发过程
  4. 纯C++编写Win32/X64通用Shellcode注入csrss进程
  5. Mediacoder基本教程
  6. UE虚幻引擎云渲染支持8K方案
  7. Altium designer—各类USB封装大全
  8. 解读《百度网页质量白皮书》潜台词
  9. PDF417数据容量
  10. FishC《零基础学习python》笔记-- 第014讲、15讲、16讲:字符串:各种奇葩的内置方法、格式化;序列