C++学习
- 1、类和对象
- P109 析构函数调用规则
- p110 深拷贝与浅拷贝
- p111 初始化列表
- p112 类对象作为类成员
- p113 静态成员
- p114 成员变量与成员函数分开存储
- p115 this指针(没太听懂¥¥¥¥¥¥¥)
- **按引用返回,返回是对象本身。。。按值方式返回,返回的不是对象本体,而是调用拷贝(负责)构造函数新建的一个对象,见复习1文档的最后内容**
- **按引用返回,返回是对象本身。。。按值方式返回,返回的不是对象本体,而是调用拷贝(负责)构造函数新建的一个对象,见复习1文档的最后内容**
- **按引用返回,返回是对象本身。。。按值方式返回,返回的不是对象本体,而是调用拷贝(负责)构造函数新建的一个对象,见复习1文档的最后内容**
- p116 空指针访问成员函数
- p117 Const修饰成员函数
- p118 全局函数 做友元
- p119 让类做友元
- p120 成员函数做友元
- p121 加法运算符重载
- p122 左移运算符重载
- p123 递增运算符重载
- p124 赋值运算法重载
- p125 关系运算符重载
- p126函数调用运算符重载
- p127 继承-基本语法(重点)
- p128继承方式
- p129 继承中的对象模型
- p130 构造和析构顺序
- p131 继承中同名函数处理方式
- p132 继承同名静态成员函数处理方式
- p133 多继承语法
- p134 菱形继承或者钻石继承
- p135 多态的基本语法
- p136
- p137 多态案例
- p138 纯虚函数和抽象类
- p139 多态 案例
- p140 虚析构 纯虚析构
- p142 案例
- p143文件操作-文本文件-写文件
- p144文件操作-文本文件-读文件
- p145 p146 二进制文件的读取和写入
1、类和对象
P109 析构函数调用规则
因为默认构造函数对其属性进行了拷贝
p110 深拷贝与浅拷贝
复制(拷贝)构造函数,形参必须是本类对象的引用。若类中没有定义复制构造函数,系统会定义一个默认的复制构造函数,完成将一个对象的所有数据成员复制(拷贝)到另一个对象的相应数据成员中。也就是浅拷贝。
复制(拷贝)构造函数,形参必须是本类对象的引用。若类中没有定义复制构造函数,系统会定义一个默认的复制构造函数,完成将一个对象的所有数据成员复制(拷贝)到另一个对象的相应数据成员中。也就是浅拷贝。
复制(拷贝)构造函数,形参必须是本类对象的引用。若类中没有定义复制构造函数,系统会定义一个默认的复制构造函数,完成将一个对象的所有数据成员复制(拷贝)到另一个对象的相应数据成员中。也就是浅拷贝。
复制(拷贝)构造函数,形参必须是本类对象的引用。若类中没有定义复制构造函数,系统会定义一个默认的复制构造函数,完成将一个对象的所有数据成员复制(拷贝)到另一个对象的相应数据成员中。也就是浅拷贝。
复制(拷贝)构造函数,形参必须是本类对象的引用。若类中没有定义复制构造函数,系统会定义一个默认的复制构造函数,完成将一个对象的所有数据成员复制(拷贝)到另一个对象的相应数据成员中。也就是浅拷贝。
复制(拷贝)构造函数,形参必须是本类对象的引用。若类中没有定义复制构造函数,系统会定义一个默认的复制构造函数,完成将一个对象的所有数据成员复制(拷贝)到另一个对象的相应数据成员中。也就是浅拷贝。
复制(拷贝)构造函数,形参必须是本类对象的引用。若类中没有定义复制构造函数,系统会定义一个默认的复制构造函数,完成将一个对象的所有数据成员复制(拷贝)到另一个对象的相应数据成员中。也就是浅拷贝。
深拷贝与浅拷贝
浅拷贝:编译器做的简单赋值操作叫做浅拷贝
堆区开辟的数据由程序员手动开辟,也需要程序员手动释放
test01() 执行完 对象p1p2就被销毁了
在对象p1p2销毁前需要将堆区数据释放,p1p2对象销毁前会调用 析构函数
因此可以使用析构函数释放堆区数据
因为p1p2是局部变量(对象),所以在栈上,栈上的规则就是先进后出
因为p1p2是局部变量(对象),所以在栈上,栈上的规则就是先进后出
因为p1p2是局部变量(对象),所以在栈上,栈上的规则就是先进后出
因为p1p2是局部变量(对象),所以在栈上,栈上的规则就是先进后出
因为p1p2是局部变量(对象),所以在栈上,栈上的规则就是先进后出
因为p1p2是局部变量(对象),所以在栈上,栈上的规则就是先进后出
所以对象p2先被释放,先执行p2的析构函数
编译的会出现问题,出现问题的原因如下::
tips:vscode可能对于这个例子不会蹦
存放的数据一样,但是指针指向的内存不一样
重新在堆区找块内存存放,也就是说存放的内容(160)一样,但是内存也就是地址不一样
这就叫深拷贝
自定义编写拷贝构造函数,完成深拷贝。
自定义编写拷贝构造函数,完成深拷贝。
自定义编写拷贝构造函数,完成深拷贝。
自定义编写拷贝构造函数,完成深拷贝。
自定义编写拷贝构造函数,完成深拷贝。
#include <iostream>
using namespace std;
class Person
{
public:
//无参(默认)构造函数
Person()
{
cout << "无参构造函数,也就是默认构造函数" << endl;
}
//有参构造函数
Person(int age,int height)
{
m_Age = age;
m_Height = new int(height);
cout << "有参构造函数" << endl;
}
//拷贝构造函数 深拷贝解决浅拷贝的问题
Person(const Person &p)
{
cout << "Person的拷贝构造函数使用" << endl;
m_Age = p.m_Age;
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(10, 160);
cout << p1.m_Age << "\t" << *p1.m_Height << endl;
Person p2(p1);
cout << p2.m_Age << "\t" << *p2.m_Height << endl;
}
int main()
{
test01();
return 0;
}
这样的话,析构的时候,各自释放各自的堆区内存,因为内存不一样了。所以肯定不会出现重复释放内存(也就是地址)的问题
浅拷贝是编译器根据默认拷贝构造函数自动完成的
p111 初始化列表
#include <iostream>
using namespace std;
class Person
{
public:
// //传统的初始化方式
// //也就是有参构造函数
// Person(int a, int b, int c)
// {
// m_a = a;
// m_b = b;
// m_c = c;
// }
// //初始化列表进行初始化属性
// Person() : m_a(20), m_b(2574), m_c(199)
// {
// }
//更加灵活 初始化列表进行初始化属性
Person(int a, int b, int c) : m_a(a), m_b(b), m_c(c)
{
}
int m_a;
int m_b;
int m_c;
};
void test01()
{
Person p1(3,2,41);
cout << p1.m_a << "\t" << p1.m_b << "\t" << p1.m_c << endl;
}
int main()
{
test01();
return 0;
}
p112 类对象作为类成员
A是类(也就是自定义的数据类型)
a是对象成员,数据类型为A
对象成员也可以使用初始化列表的方式进行初始化,可能会使用到构造函数调用中的 隐世转换法
#include <iostream>
using namespace std;
#include <string>
//手机类
class Phone
{
public:
Phone(string name) :m_pName(name)
{
cout << "手机类构造函数的调用" << endl;
}
~Phone()
{
cout << "Phone的析构函数" << endl;
}
string m_pName;
};
//人类
class Person
{
public:
//后面那个初始化相当于Phone m_phone = pname 也就是隐世转换调用
Person(string name, string pname) :m_name(name), m_phone(pname)
{
cout << "人类构造函数的调用" << endl;
}
~Person()
{
cout << "Person的析构函数" << endl;
}
//姓名
string m_name;
//手机
Phone m_phone;
};
void test01()
{
Person p1("张帅", "华为");
cout << p1.m_name << "使用" << p1.m_phone.m_pName << endl;
}
int main()
{
test01();
return 0;
}
当其他类对象作为本类成员时,构造时先构造其他类对象,再构造本类对象,析构的顺序与构造的顺序相反
p113 静态成员
1、所有对象共享一份数据,对象a的这个数据变化后,对象b调用这个数据时也变化了。
2、在编译阶段分配内存:也就是指:程序还没有运行之前,(程序编译后,)生成exe还没有双击之前就已经分配好内存了,放在全局区里面
静态成员变量,不属于某个对象上,所有的对象都共享同一份数据
静态成员变量,不属于某个对象上,所有的对象都共享同一份数据
静态成员变量,不属于某个对象上,所有的对象都共享同一份数据
教程连接
#include <iostream>
using namespace std;
#include <string>
class Person
{
public:
static int m_a;
};
int Person::m_a = 10;
void test01()
{
Person p1;
cout << p1.m_a << endl;
Person p2;
p2.m_a = 200;
cout << p1.m_a << endl;
}
void test02()
{
//1、通过对象进行访问
Person p3;
cout << p3.m_a << endl;
//2、通过类名进行访问
cout << Person::m_a << endl;
}
int main()
{
test02();
return 0;
}
p114 成员变量与成员函数分开存储
p115 this指针(没太听懂¥¥¥¥¥¥¥)
this指针指向 被调用的成员函数所属的对象
this指针指向 被调用的成员函数所属的对象
this指针指向 被调用的成员函数所属的对象
this指针指向 被调用的成员函数所属的对象
this指针指向 被调用的成员函数所属的对象
this指针指向 被调用的成员函数所属的对象
this指针指向 被调用的成员函数所属的对象
1、解决名称冲突
2、返回对象本身用*this
按引用返回,返回是对象本身。。。按值方式返回,返回的不是对象本体,而是调用拷贝(负责)构造函数新建的一个对象,见复习1文档的最后内容
按引用返回,返回是对象本身。。。按值方式返回,返回的不是对象本体,而是调用拷贝(负责)构造函数新建的一个对象,见复习1文档的最后内容
按引用返回,返回是对象本身。。。按值方式返回,返回的不是对象本体,而是调用拷贝(负责)构造函数新建的一个对象,见复习1文档的最后内容
我明白了
之所以用引用返回的方式,是因为叠加是在原数据(也就是p2)改变的基础上递增,所以用引用。
如果去掉引用返回,也就是去掉&后,每一次调用都会返回一个新的对象(因为值方式返回的原因,每一次调用后返回的都是一个新的对象,调用了拷贝构造函数,最后也就不是p2了,而是p2\\)
如下:
所以最后输出的时候,p2.age只加了一次而不是三次。
总结:此例子中,如果值方式返回则:会创建新的对象。如果引用方式返回,则:不会创建新的对象,一直都是p2。
有了*this 就可以使用连式编程思想进行编程
有了*this 就可以使用连式编程思想进行编程
有了*this 就可以使用连式编程思想进行编程
有了*this 就可以使用连式编程思想进行编程
#include <iostream>
using namespace std;
class Person
{
public:
Person(int age)
{
cout << "有参构造函数" << endl;
//**this指针指向 被调用的成员函数所属的对象**
//解决名称冲突 this指针的作用之一
this->age = age;
}
//引用的方式返回
Person & PersonAddage(Person &p)
{
this->age += p.age;
return *this;
}
~Person()
{
cout << "析构函数" << endl;
}
int age;
};
void test01()
{
Person p1(10);
Person p2(25);
p2.PersonAddage(p1).PersonAddage(p1).PersonAddage(p1);
cout << p2.age << endl;
}
int main()
{
test01();
return 0;
}
void test01()
{
Person p1(10);
Person p2(25);
Person &p3 = p2;
p3=p2.PersonAddage(p1);
p3.PersonAddage(p1).PersonAddage(p1);
cout << p2.age << endl;
cout << p3.age << endl;
}
p116 空指针访问成员函数
p117 Const修饰成员函数
下图的函数即为常函数:
p118 全局函数 做友元
第一种情况:用全局函数作为好朋友
只要上面的那条语句写到类中最上面就可以了,也不需要public啥的
#include <iostream>
#include <string>
using namespace std;
class Building
{
//放在类中最前面,这个全局函数是Building类的好朋友,可以访问其私有成员
friend void goodGay(Building *build);
public:
//无参构造函数
Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
string m_SittingRoom;
private:
string m_BedRoom;
};
//全局函数
void goodGay(Building *build)
{
cout << build->m_SittingRoom << endl;
cout << build->m_BedRoom << endl;
}
void test01()
{
Building build1;
goodGay(&build1);
}
int main()
{
test01();
return 0;
}
p119 让类做友元
第二中情况,让类做友元
#include <iostream>
#include <string>
using namespace std;
class Building;//先声明类Building
class GoodGay//定义类
{
public:
GoodGay();
void visit();
Building *building;
};
class Building
{
//GoodGay类是本类的好朋友,
//其函数可以访问Building类的私有成员 公有成员本来就可以访问
friend class GoodGay;
public:
Building();
string m_SittingRoom;
private:
string m_BedRoom;
};
//类外写成员函数
GoodGay::GoodGay()
{
//创建建筑物对象
building = new Building;
}
//类外写成员函数
void GoodGay::visit()
{
cout << "正在访问" << building->m_SittingRoom << endl;
cout << "正在访问" << building->m_BedRoom << endl;
}
//类外写成员函数
Building::Building()
{
m_BedRoom = "卧室";
m_SittingRoom = "客厅";
}
void test01()
{
GoodGay gg;
gg.visit();
}
int main()
{
test01();
return 0;
}
p120 成员函数做友元
第三种情况: 让成员函数做友元
成员函数类外实现或类内实现都可以
#include <iostream>
#include <string>
using namespace std;
class Building;//先声明类Building
class GoodGay//定义类
{
public:
GoodGay();
void visit1(); //让visit函数可以访问Building中的私有成员
void visit2();//让visit2函数不可以访问Building中的私有成员
Building *building;
};
class Building
{
friend void GoodGay::visit1();
public:
Building();
string m_SittingRoom;
private:
string m_BedRoom;
};
//类外写成员函数
GoodGay::GoodGay()
{
//创建建筑物对 创建一个Building对象在堆区
building = new Building;
}
//类外写成员函数
void GoodGay::visit1()
{
cout << "visit1正在访问" << building->m_SittingRoom << endl;
cout << "visit1正在访问" << building->m_BedRoom << endl;
}
void GoodGay::visit2()
{
cout << "visit2正在访问" << building->m_SittingRoom << endl;
// cout << "正在访问" << building->m_BedRoom << endl;
}
//类外写成员函数
Building::Building()
{
m_BedRoom = "卧室";
m_SittingRoom = "客厅";
}
void test01()
{
GoodGay gg;
gg.visit1();
gg.visit2();
}
int main()
{
test01();
return 0;
}
如果不是友元,那么只能访问公共属性
p121 加法运算符重载
两种重载方式,一种是成员函数,另一种是全局函数
#include <iostream>
using namespace std;
class Person
{
public:
// //在public下
// //方式1:通过成员函数重载+号
// 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 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;
}
//函数重载的版本
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, p2;
p1.m_A = 11;
p1.m_B = 22;
p2.m_A = 11;
p2.m_B = 22;
Person p3;
p3 = p1 + p2;
cout << p3.m_A << "\t" << p3.m_B << endl;
Person p4 = p1 + 10;
cout << p4.m_A << "\t" << p4.m_B << endl;
}
int main()
{
test01();
return 0;
}
两种方式的重载本质调用
运算符也可以发生函数重载
函数重载的概念回忆一下::
p122 左移运算符重载
一般不使用第一种方式,也就是成员函数实现重载,原因如下:
cout:标准的输出流对象 类:ostream
cin: 标准的输入流对象 类:istream
因为cout对象全局只有一个,所以需要引用的方式传递进来,值传递的话,就会创建(复制一个副本)一个新的对象。
输出流不能拷贝,也就是说要传入输出流对象参数只能地址传递,不能值传递。
流对象不能复制只能引用,记住就好了
输出流不能拷贝,也就是说要传入输出流对象参数只能地址传递,不能值传递。
流对象不能复制只能引用,记住就好了
输出流不能拷贝,也就是说要传入输出流对象参数只能地址传递,不能值传递。
流对象不能复制只能引用,记住就好了
输出流不能拷贝,也就是说要传入输出流对象参数只能地址传递,不能值传递。
流对象不能复制只能引用,记住就好了
输出流不能拷贝,也就是说要传入输出流对象参数只能地址传递,不能值传递。
流对象不能复制只能引用,记住就好了
输出流不能拷贝,也就是说要传入输出流对象参数只能地址传递,不能值传递。
流对象不能复制只能引用,记住就好了
#include <iostream>
using namespace std;
class Person
{
public:
int m_A;
int m_B;
};
//使用全局函数的方式重载左移运算符
//输出流对象 cout使用引用的方式传递
//注意这里的ostream & 返回类型可以确保返回的是cout本体
ostream & operator<<(ostream & cout,Person &p)
{
cout << "p.m_A:" << p.m_A << " "
<< "p.m_B:" << p.m_B << endl;
return cout;
}
void test01()
{
Person p1;
p1.m_A = 15;
p1.m_B = 25;
cout << p1 << endl;
;
}
int main()
{
test01();
return 0;
}
#include <iostream>
using namespace std;
class Person
{
friend ostream &operator<<(ostream &cout, Person &p);
private:
int m_A;
int m_B;
};
//使用全局函数的方式重载左移运算符
//输出流对象 cout使用引用的方式传递
//注意这里的ostream & 返回类型可以确保返回的是cout本体
ostream & operator<<(ostream & cout,Person &p)
{
p.m_A = 15;
p.m_B = 25;
cout << "p.m_A:" << p.m_A << " "
<< "p.m_B:" << p.m_B << endl;
return cout;
}
void test01()
{
Person p1;
cout << p1 << endl;
}
int main()
{
test01();
return 0;
}
p123 递增运算符重载
内置的递增++运算符:
前置递增 先对变量进行递增,再进行其他操作。
后置递增,先对变量进行完其他操作,再进行变量递增。
1、前置递增
返回引用是为了一直对一个对象进行递增操作,否则的话,如果不是返回引用,而是返回值,则每次递增后返回一个新的对象(调用拷贝构造函数,如果没有自己定义的,会进行浅拷贝)。
返回引用是为了一直对一个对象进行递增操作,否则的话,如果不是返回引用,而是返回值,则每次递增后返回一个新的对象(调用拷贝构造函数,如果没有自己定义的,会进行浅拷贝)。
返回引用是为了一直对一个对象进行递增操作,否则的话,如果不是返回引用,而是返回值,则每次递增后返回一个新的对象(调用拷贝构造函数,如果没有自己定义的,会进行浅拷贝)。
哔哩哔哩链接
对象作为返回值不加引用就会调用拷贝构造函数
对象作为返回值不加引用就会调用拷贝构造函数
对象作为返回值不加引用就会调用拷贝构造函数
对象作为返回值不加引用就会调用拷贝构造函数
对象作为返回值不加引用就会调用拷贝构造函数
对象作为返回值不加引用就会调用拷贝构造函数
对象作为返回值不加引用就会调用拷贝构造函数
对象作为返回值不加引用就会调用拷贝构造函数
复习知识点:拷贝构造函数的调用时机
值返回是相当于对一个新的返回,再操作又继续开个新的重复
2、后置递增
返回值不可以作为函数重载的条件
函数占位参数
此例中的移位与上一节课中的不太一样
后置++使用移位运算符重载 不能定义引用 ,因为返回的临时对象temp已被编译器释放,因此只能值传递
#include <iostream>
using namespace std;
class MyInteger
{
friend ostream &operator<<(ostream &cout, MyInteger p);
public:
MyInteger()
{
m_int = 0;
}
//前置++ 通过成员函数重载
//返回的本体对象
MyInteger & operator++()
{
++m_int;
return *this;
}
//后置递增 通过成员函数重载实现
//返回的是局部对象 不能使用引用返回,因为temp为局部对象,后置递增执行完就销毁了temp
MyInteger operator++(int)
{
MyInteger temp=*this;
m_int++;
return temp;
}
private:
int m_int;
};
//使用全局函数的方式重载左移运算符
//形参中输出流对象 cout使用引用的方式传递
//注意这里的ostream & 返回类型可以确保返回的是cout本体
//MyInteger p
//后置++使用移位运算符重载 不能定义引用 ,因为返回的临时对象temp已被编译器释放,因此只能值传递
ostream & operator<<(ostream & cout,MyInteger p)
{
cout << p.m_int << endl;
return cout;
}
void test01()
{
MyInteger m_int1;
cout << ++(++m_int1) << endl;
cout << m_int1 << endl;
}
void test02()
{
MyInteger m_int2;
cout << m_int2++ << endl;
cout << m_int2 << endl;
}
int main()
{
test01();
test02();
return 0;
}
p124 赋值运算法重载
一般来说,只要是值拷贝,都会引发深浅拷贝问题
回忆:拷贝构造函数
让p2=p1之后,再把p2这个对象传给p3,也就是函数operator=调用完之后还能返回自身(使用return *this返回自身)
返回本体需要时引用返回,而不是值返回
此例中若返回值的话,那就是按照自身调用一个拷贝构造函数(默认浅拷贝),创建一个新的对象(副本),而不是对象自身了。我们要返回其引用才是其真正的自身。
#include <iostream>
using namespace std;
class Person
{
public:
//默认构造函数
Person(int age)
{
m_Age = new int(age);
}
//析构函数
~Person()
{
if(m_Age!=NULL)
{
delete m_Age;
m_Age = NULL;
}
}
//重载赋值运算符
Person & operator=(Person &p)
{
if (m_Age!=NULL)
{
delete m_Age;
m_Age = NULL;
}
m_Age = new int(*p.m_Age);
//指向自身的指针是this指针,*解引用
return *this;//函数调用之后还能返回对象的自身
}
int *m_Age;//这个变量因为是指针,所以被开辟到堆区,而不是栈区
};
void test01()
{
Person p1(18);
Person p2(22);
Person p3(33);
p3=p2 = p1;
cout << "p1的年龄"<<*p1.m_Age<<endl;
cout << "p2的年龄"<<*p2.m_Age<<endl;
cout << "p3的年龄"<<*p3.m_Age<<endl;
}
int main()
{
int a = 10, b = 20, c = 30;
c = b = a;
cout << "a=" << a << endl;
cout << "b=" << b << endl;
cout << "c=" << c << endl;
test01();
return 0;
}
p125 关系运算符重载
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
Person(string name,int age)
{
m_Age = age;
m_Name = name;
}
bool operator==(Person &p)
{
if (this->m_Age==p.m_Age && this->m_Name==p.m_Name)
{
return true;
}
else
{
return false;
}
}
string m_Name;
int m_Age;
};
void test01()
{
Person p1("tom1", 22);
Person p2("tom", 22);
if(p1==p2)
{
cout << "yes" << endl;
}
else
{
cout << "no" << endl;
}
}
int main()
{
test01();
return 0;
}
p126函数调用运算符重载
仿函数
十分灵活
匿名函数对象
#include <iostream>
#include <string>
using namespace std;
class Myprint
{
public:
//重载函数调用运算符
void operator()(string test)
{
cout << test << endl;
}
};
void test01()
{
Myprint myprint;
myprint("helloworld");
}
class Add
{
public:
void operator()(int num1,int num2)
{
cout << num1 + num2 << endl;
}
};
void test02()
{
Add add;
add(1, 1);
Add()(100, 125);//匿名函数对象,Add()相当于匿名对象
}
int main()
{
// test01();
test02();
return 0;
}
p127 继承-基本语法(重点)
继承的技术
#include <iostream>
using namespace std;
//父类 也就是基类
class Base
{
public:
void header()
{
cout << "首页、公开课、登录(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心、联系方式(公共底部)" << endl;
}
void leftlist()
{
cout << "C++、Python、Java(公共左侧)" << endl;
}
};
//Java继承Base类
class Java : public Base
{
public:
void content()
{
cout << "Java 视频资料" << endl;
}
};
//cpp继承Base类
class Cpp : public Base
{
public:
void content()
{
cout << "cpp 视频资料" << endl;
}
};
void test01(Java &p)
{
cout << "java下载视频的页面" << endl;
p.header();
p.footer();
p.leftlist();
p.content();
}
void test02(Cpp &p)
{
cout << "cpp下载视频的页面" << endl;
p.header();
p.footer();
p.leftlist();
p.content();
}
int main()
{
Java java1;
Cpp cpp1;
test01(java1);
cout << "-----------------" << endl;
test02(cpp1);
return 0;
}
p128继承方式
p129 继承中的对象模型
开发人员命令提示工具
#include <iostream>
using namespace std;
class Base
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son1 : public Base
{
public:
int m_D;
};
int main()
{
int a = 10;
Base base;
Son1 son1;
cout << sizeof(a) << endl;
cout << sizeof(base) << endl;
cout << sizeof(son1) << endl;
}
p130 构造和析构顺序
p131 继承中同名函数处理方式
注意是所有的同名函数(包括重载的 都给隐藏掉了)
#include <iostream>
using namespace std;
class Base
{
public:
Base()
{
m_A = 10;
}
void func()
{
cout << "Base func" << endl;
}
void func(int a)
{
cout << "Son int a func" << endl;
}
int m_A;
};
class Son: public Base
{
public:
Son()
{
m_A = 100;
}
void func()
{
cout << "Son func" << endl;
}
int m_A;
};
//调用同名成员数据
void test01()
{
Son son1;
cout << "son的m_A "<< son1.m_A << endl;
cout << "父类的m_A " << son1.Base::m_A << endl;
}
//调用同名成员函数
void test02()
{
Son son2;
son2.func();//同名情况下,调用子类的,加类作用域才调用父类的
son2.Base::func();
//错误,因为子类和父类的成员函数重名后,父类的所以这个名称的成员函数被隐藏掉
//找不到第二个父类的func(int a)函数
// son2.func(10);
//加上类作用域就可以啦
son2.Base::func(10);
}
int main()
{
test02();
return 0;
}
p132 继承同名静态成员函数处理方式
复习 类与对象中的静态成员
静态成员的访问有两种方式:通过对象访问或者通过类名。
静态成员的访问有两种方式:通过对象访问或者通过类名。
静态成员的访问有两种方式:通过对象访问或者通过类名。
静态成员的访问有两种方式:通过对象访问或者通过类名。
静态成员的访问有两种方式:通过对象访问或者通过类名。
静态成员的访问有两种方式:通过对象访问或者通过类名。
静态成员的访问有两种方式:通过对象访问或者通过类名。
静态成员的访问有两种方式:通过对象访问或者通过类名。
静态成员的访问有两种方式:通过对象访问或者通过类名。
1、通过对象访问
2、通过类名访问
#include <iostream>
using namespace std;
class Base
{
public:
static int m_A;
static void fun()
{
cout << "base func" << endl;
}
static void fun(int a)
{
cout << "base func int a" << endl;
}
};
int Base::m_A = 10;
class Son :public Base
{
public:
static int m_A;
static void fun()
{
cout << "son fun" << endl;
}
};
int Son::m_A = 100;
void test01()
{
Son son;
//方式一通过对象来访问静态成员数据
cout << "通过对象访问" << endl;
cout << "son :" << son.m_A << endl;
cout << "base :" << son.Base::m_A << endl;
//方式二 通过类名访问静态成员数据
cout << "通过类名访问静态成员数据" << endl;
cout << "son :" << Son::m_A << endl;
cout << "Base :" << Base::m_A << endl;
//通过访问son中的父类作用下的m_A
cout << "Base ::" << Son::Base::m_A << endl;
}
void test02()
{
//1.
Son son;
son.fun();
son.Base::fun();
//2.
Son::fun();
Son::Base::fun();
Son::Base::fun(10255);
}
int main()
{
test02();
return 0;
}
p133 多继承语法
作用域::
p134 菱形继承或者钻石继承
【暂时没学习,用到的时候再学习】
p135 多态的基本语法
父类引用指向子类对象
c++中运行父子类之间类型转换
静态多态
这样就可以实现地址晚绑定 也就是动态多态
函数重写的意思:(子类的virtual可写可不写)
总结:
#include <iostream>
using namespace std;
//父类或者基类
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 &p)//父类指针或者引用指向子类对象
{
p.speak();//现在speak等于是多种形态了
}
void test01()
{
Cat cat1;
DoSpeak(cat1);
Dog dog1;
DoSpeak(dog1);
}
int main()
{
test01();
return 0;
}
p136
四个字节一般是整形变量 指针等
当用父类指针或者引用指向子类对象,调用speak时会调用小猫会说话。
p137 多态案例
#include <iostream>
using namespace std;
//多态的使用
//父类 抽象的一个类
class AbstractCalculator
{
public:
virtual int GetResult()
{
return 0;
}
int m_Num1;
int m_Num2;
};
//加法
class AddCalculator : public AbstractCalculator
{
public:
int GetResult()
{
return m_Num1 + m_Num2;
}
};
// 减法
class SubCalculator : public AbstractCalculator
{
public:
int GetResult()
{
return m_Num1 - m_Num2;
}
};
// 乘法
class MulCalculator : public AbstractCalculator
{
public:
int GetResult()
{
return m_Num1 * m_Num2;
}
};
void test01()
{
//实现加法
AbstractCalculator *abc = new AddCalculator;
abc->m_Num1 = 15;
abc->m_Num2 = 35;
cout << abc->GetResult() << endl;
//用完后记得销毁
delete abc;
abc = new SubCalculator;
abc->m_Num1 = 100;
abc->m_Num2 = 90;
cout << abc->GetResult() << endl;
}
int main()
{
test01();
return 0;
}
p138 纯虚函数和抽象类
p139 多态 案例
**
**
#include <iostream>
using namespace std;
//多态的使用
//多态案例2 制作饮品
//基类 也就是父类,因为里面的成员函数有纯虚函数,所以变成抽象类
class AbstractDrinking
{
public:
//成员函数写成纯虚函数 基类也就变成抽象类
//煮水
virtual void Boil() = 0;
//冲泡
virtual void Brew() = 0;
//倒入杯子
virtual void PourInCup() = 0;
//加入辅料
virtual void PutSomething() = 0;
//制作饮品
void MakeDrinking()
{
Boil();
Brew();
PourInCup();
PutSomething();
}
};
//制作咖啡
class MakeCoffee : public AbstractDrinking
{
public:
//成员函数写成纯虚函数 基类也就变成抽象类
//煮水
void Boil()
{
cout << "煮水" << endl;
}
//冲泡
void Brew()
{
cout << "冲泡咖啡" << endl;
}
//倒入杯子
void PourInCup()
{
cout << "将咖啡倒入杯中" << endl;
}
//加入辅料
void PutSomething()
{
cout << "将辅料加入到咖啡中" << endl;
}
};
//制作茶水
class MakeTea : public AbstractDrinking
{
public:
//成员函数写成纯虚函数 基类也就变成抽象类
//煮水
void Boil()
{
cout << "煮水" << endl;
}
//冲泡
void Brew()
{
cout << "冲泡茶叶" << endl;
}
//倒入杯子
void PourInCup()
{
cout << "将茶水倒入杯中" << endl;
}
//加入辅料
void PutSomething()
{
cout << "将辅料加入到茶水中" << endl;
}
};
void DoWork(AbstractDrinking *abs)
{
abs->MakeDrinking();
}
void test01()
{
AbstractDrinking *abs1 = new MakeCoffee;
DoWork(abs1);
// DoWork(new MakeCoffee);
delete abs1;
abs1 = new MakeTea;
DoWork(abs1);
// DoWork(new MakeTea);
delete abs1;
}
int main()
{
test01();
return 0;
}
p140 虚析构 纯虚析构
纯虚析构函数需要注意的问题
在多态使用中:
不管是虚析构还是纯虚析构 都是为了解决子类中析构代码调用不到的问题
p142 案例
#include <iostream>
using namespace std;
//多态的使用
//多态案例
//三个抽象类
//cpu抽象类
class Cpu
{
public:
virtual void Calculator() = 0;
};
//显卡抽象类
class VideoCard
{
public:
virtual void display() = 0;
};
//存储抽象类
class Memory
{
public:
virtual void storage() = 0;
};
//电脑类
class Computer
{
public:
//构造函数
Computer(Cpu *cpu, VideoCard *vc, Memory *mem)
{
cout << "Computer的构造" << endl;
m_cpu = cpu;
m_vc = vc;
m_mem = mem;
}
void Work()
{
m_cpu->Calculator();
m_mem->storage();
m_vc->display();
}
//析构函数
~Computer()
{
cout << "Computer的析构" << endl;
if(m_cpu!=NULL)
{
delete m_cpu;
m_cpu = NULL;
}
if(m_vc!=NULL)
{
delete m_vc;
m_vc = NULL;
}
if(m_mem!=NULL)
{
delete m_mem;
m_mem = NULL;
}
}
private :
Cpu *m_cpu;
VideoCard *m_vc;
Memory *m_mem;
};
//Intel厂商
class IntelCpu : public Cpu
{
public:
//派生类重写函数
void Calculator()
{
cout << "Intel的cpu开始计算了" << endl;
}
};
class IntelVideoCard : public VideoCard
{
public:
void display()
{
cout << "Intel的显卡开始工作了" << endl;
}
};
class IntelMemory : public Memory
{
public:
void storage()
{
cout << "Intel的内存开始工作了" << endl;
}
};
//Lenovo厂商
class LenovoCpu : public Cpu
{
public:
void Calculator()
{
cout << "Lenovo的cpu开始计算了" << endl;
}
};
class LenovoVideoCard : public VideoCard
{
public:
void display()
{
cout << "Lenovo的显卡开始工作了" << endl;
}
};
class LenovoMemory : public Memory
{
public:
void storage()
{
cout << "Lenovo的内存开始工作了" << endl;
}
};
void test01()
{
//第一台电脑零件
//多态的条件之一,父类指针指向子类对象
//堆区的数据手动开启 需要手动释放!!!!!!
Cpu *intelCpu = new IntelCpu;
VideoCard *videoCard = new IntelVideoCard;
Memory *memory = new IntelMemory;
//创建第一台电脑
Computer *computer = new Computer(intelCpu, videoCard, memory);
computer->Work();
//释放下面这个对象时 执行器析构函数
delete computer;
//创建第二台电脑 换了个写法 直接在实参出new ,,,
Computer *computer2 = new Computer(new LenovoCpu, new LenovoVideoCard, new LenovoMemory);
computer2->Work();
delete computer2;
}
int main()
{
test01();
return 0;
}
p143文件操作-文本文件-写文件
五步法写文件
#include <iostream>
using namespace std;
#include <fstream>
//练习写文件
void test01()
{
//1.包含头文件
//2.创建流对象
ofstream ofs;
//3.指定打开方式
ofs.open("text.txt", ios::out);
//4.写内容 换行照样好使
ofs << "sdawsd" << endl;
ofs << "张帅克拉拉啊" << endl;
//5.关闭文件
ofs.close();
}
int main()
{
test01();
return 0;
}
p144文件操作-文本文件-读文件
读文件共有四种方式,第四种如下:
#include <iostream>
using namespace std;
#include <fstream>
#include <string>
//练习读文件
void test01()
{
//1.包含头文件
//2.创建流对象
ifstream ifs;
//3.指定打开方式
ifs.open("text.txt", ios::in);
//判断文件是否打开成功
if(!ifs.is_open())
{
cout << "文件打开失败" << endl;
return;
}
//4.读文件
//第一种
// char buf[1024] = {0};
// while (ifs>>buf)
// {
// cout << buf << endl;
// }
cout << "---------" << endl;
//第二种方式
// char buf[1024] = {0};
// while(ifs.getline(buf,sizeof(buf)))
// {
// cout << buf << endl;
// }
//第三种
string buf;//字符串变量
while(getline((ifs),buf))
{
cout << buf << endl;
}
//5.关闭文件
ifs.close();
}
int main()
{
test01();
return 0;
}
p145 p146 二进制文件的读取和写入
=暂时未学习