例如我们看到很多网站中,都有公共的头部,公共的底部,甚至公共的左侧列表,只有中心内容不同,接下来我们分别利用普通写法和继承的写法来实现网页中的内容,看一下继承存在的意义以及好处。
#include6.2 继承方式using namespace std; //继承实现页面 这个是三个学科都共同拥有的部分 class basepage { public: void header() { cout << "首页、公开课、登录、注册...(公共头部)" << endl; } void footer() { cout <<"帮助中心、交流合作、站内地图...(公共底部)" << endl; } void left() { cout << "JAVA、Python、c++...(公共分类列表)" << endl; } }; //继承好处 : 减少重复代码 //语法: class 子类 : 继承方式 父类 //子类 也称为 派生类 //父类 也称为 基类 //java页面 class java :public basepage //语法切记 { public: void content() { cout <<"java学科视频" << endl; } }; //python页面 class python :public basepage { public: void content() { cout <<"python学科视频" << endl; } }; //c++页面 class cpp :public basepage { public: void content() { cout << "cpp学科视频" << endl; } }; void test01() { cout << "java 下载视频页面如下" << endl; java ja; ja.content(); ja.footer(); ja.header(); ja.left(); cout << " _________________________________________________" << endl; cout << "python 下载视频页面如下" << endl; python py; py.content(); py.footer(); py.header(); cout << " _________________________________________________" << endl; py.left(); cout << "java 下载视频页面如下" << endl; cpp cp; cp.content(); cp.footer(); cp.header(); cp.left(); } int main() { test01(); system("pause"); return 0; }
继承的语法:class子类∶继承方式父类
继承方式—共有三种:
公共继承 保护继承 私有继承
太简单了,三种权限,不同调用的时候都有不同的访问权限,记住即可。
#include6.3 继承中的对象模型using namespace std; //继承方式 class base1 //三种变量 { public: int m_a; protected: int m_b; private: int m_c; }; //公共继承 class son1 : public base1 { public: void func() { m_a = 10; //父类中的公共权限成员到子类中依然是公共权限 m_b = 10; //保护权限的成员也可以的 //m_c = 10; //私有没有权限了!! } }; void test01() { son1 s1; s1.m_a = 100; //s1.m_b = 100; //虽然可以修改保护成员,但是类外不可以访问protected的成员。 } //保护继承 class son2 : protected base1 { public: void func() { m_a = 10; //父类中的保护权限成员到子类中依然是公共权限 m_b = 10; //保护权限的成员也可以的 //m_c = 10; //私有没有权限了!! } }; void test02() { son2 s2; //s2.m_a = 100;//类外不可以访问protected的成员。 //s1.m_b = 100; //类外不可以访问protected的成员。 } //私有继承 class son3 : private base1 { public: void func() { m_a = 10; //私有权限 m_b = 10; //私有权限 //m_c = 10; //父类的私有你就没有权限了!! } }; void test03() { son3 s3; //s3.m_a = 100;//类外不可以访问protected的成员。 //s3.m_b = 100; //类外不可以访问protected的成员。 } int main() { test03(); system("pause"); return 0; }
问题: 从父类继承过来的成员,哪些属于子类对象中?
#includeusing namespace std; // class base1 { public: int m_a; protected: int m_b; private: int m_c; }; class son : public base1 { public: int m_d; }; //利用开发人员命令提示工具查看对象模型 //跳转盘符 F: // 跳转文件路径 cd 具体路径下 // 查看命名 //cl /d1 reportSingleClassLayout类名 文件名 // void test01() { //16 //父类中的所有非静态成员属性都会被子类继承下去 //父类中私有成员属性, 是被编译器隐藏了,因此是访问不到的,但是确实是被继承下去了 cout < 6.4 继承中构造和析构顺序 子类继承父类后,当创建子类对象,也会调用父类的构造函数
问题:父类和子类的构造和析构顺序是谁先谁后?#include6.5 继承同名成员处理方式using namespace std; // class base { public: base() { cout << "base构造函数" << endl; } ~base() { cout << "base析构函数" << endl; } }; class son : public base { public: son() { cout << "son构造函数" << endl; } ~son() { cout << "son析构函数" << endl; } }; void test01() { //base b; //继承中的构造和析构顺序如下: //先构造父类,再构子类;析构相反 son s; } int main() { test01(); system("pause"); return 0; } 问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?
·访问子类同名成员直接访问即可
·访问父类同名成员需要加作用域#includeusing namespace std; // class base { public: base() { m_a = 100; } void func() { cout <<"base - func()" << endl; } void func(int a) { cout << "base (int a) - func()" << endl; } int m_a; }; class son :public base { public: son() { m_a = 200; } void func() { cout << "son - func()" << endl; } int m_a; }; //同名成员属性处理方式 void test01() { son s; cout <<"son m_a = " < 6.6 继承同名静态成员处理方式 问题:继承中同名的静态成员在子类对象上如何进行访问?
静态成员和非静态成员出现同名,处理方式—致
访问子类同名成员直接访问即可
访问父类同名成员需要加作用域#includeusing namespace std; // class base { public: static int m_a; static void func() { cout << "base - static void func()" << endl; } static void func(int a) { cout << "base(int a) - static void func()" << endl; } }; int base::m_a = 100; class son : public base { public: static int m_a; static void func() { cout << "son - static void func()" << endl; } }; int son::m_a = 200; //同名静态成员属性 void test01() { //静态成员访问方式有两种 //1 通过对象访问 cout << "通过对象访问" << endl; son s; cout <<"son 下m_a =" < 6.7 多继承语法 C++允许一个类继承多个类
语法:class子类︰继承方式父类1 ,继承方式父类2…
多继承可能会引发父类中有同名成员出现,需要加作用域区分#includeusing namespace std; // class base1 { public: base1() { m_a = 100; } int m_a; }; class base2 { public: base2() { m_a = 100; } int m_a; }; //子类继承base1和base2 class son : public base1, public base2 { public: son() { m_c = 300; m_d = 400; } int m_c; int m_d; }; void test01() { son s; cout << "sizeof son"< 6.8 菱形继承
菱形继承问题:
1.羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时,就会产生二义性。
⒉草泥马继承自动物的数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。#include7 多态using namespace std; //动物类 class animal { public: int m_age; }; //利用虚继承 可以解决菱形继承的问题 // 在继承之前 加上关键字 virtual 变成虚继承 // animal 类 称为 虚基类 // //羊 class sheep :virtual public animal {}; //驼 class tuo : virtual public animal {}; //羊驼类 class sheeptuo : public sheep, public tuo {}; void test01() { sheeptuo st; st.sheep::m_age = 19; st.tuo::m_age = 15; //当菱形继承,两个父类拥有相同数据,需要加以作用域区分 cout << "sheep m_age = " << st.sheep::m_age << endl; cout << "tuo m_age = " << st.tuo::m_age << endl; cout << "st m_age = " << st.m_age << endl; //但是我们中的,我们只需要一份就可以了,菱形继承导致数据有两份,资源浪费 } int main() { test01(); system("pause"); return 0; } 多态是C++面向对象三大特性之一多态分为两类
静态多态 动态多态 函数重载和运算符重载属于静态多态,复用函数名派生类和虚函数实现运行时多态 静态多态的函数地址早绑定–编译阶段确定函数地址动态多态的函数地址晚绑定运行阶段确定函数地址 #include7.1 多态的基本概念using namespace std; //多态 //动物类 class Animal { public: virtual void speak() { cout << "动物在说话" << endl; } }; //猫类 class Cat : public Animal { public: //重写 函数返回值类型 函数名 参数列表 要完全相等 void speak() //virtual 可加可不加 { cout << "小猫在说话" << endl; } }; //狗类 class Dog : public Animal { public: void speak() { cout << "小狗在说话" << endl; } }; //执行说话的函数 //地址早绑定的话,只会输出动物在说话,因为在编译阶段 animal.speak();确定函数地址 //若想执行让猫说话,那么这个函数地址就不可以提前绑定,需要在运行阶段绑定,地址晚绑定 ,virtual void speak()这样就可以了 //动态多态满足条件 //1. 有继承关系 //2. 子类要重写父类的虚函数 //动态多态的使用 //父类的指针或者引用 执行子类的对象 void dospeak(Animal &animal) //Animal &animal = cat { animal.speak(); } void test01() { Cat cat; dospeak(cat); Dog dog; dospeak(dog); } int main() { test01(); system("pause"); return 0; } #include7.2 多态案例─计算器类using namespace std; //多态 //动物类 class Animal { public: virtual void speak() { cout << "动物在说话" << endl; } }; //猫类 class Cat : public Animal { public: //重写 函数返回值类型 函数名 参数列表 要完全相等 void speak() //virtual 可加可不加 { cout << "小猫在说话" << endl; } }; //狗类 class Dog : public Animal { public: void speak() { cout << "小狗在说话" << endl; } }; //执行说话的函数 //地址早绑定 在编一阶段确定函数地址 //若想执行让猫说话,那么这个函数地址就不可以提前绑定,需要在运行阶段绑定,地址晚绑定 //动态动态满足条件 //1. 有继承关系 //2. 子类要重写父类的虚函数 //动态多态的使用 //父类的指针或者引用 执行子类的对象 void dospeak(Animal& animal) //Animal &animal = cat { animal.speak(); } void test01() { Cat cat; dospeak(cat); Dog dog; dospeak(dog); } void test02() { cout << "sizeof(Animal)" << sizeof(Animal) << endl; //指针的大小 } int main() { test02(); system("pause"); return 0; } 案例描述:
分别利用普通写法和多态技术,设计实现两个 *** 作数进行运算的计算器类
多态的优点:│
代码组织结构清晰·可读性强
利于前期和后期的扩展以及维护#include7.3 纯虚函数和抽象类using namespace std; //普通实现 class calculate { public: int getresult(string oper) { if (oper == "+") { return m_num1 + m_num2; } else if (oper == "-") { return m_num1 - m_num2; } else if (oper == "*") { return m_num1 * m_num2; } } //如果要扩展修改功能,需要修改源码 //在真实开发中 提倡 开闭原则 //开闭原则 : 对拓展进行开放,对修改进行关闭 int m_num1; int m_num2;// *** 作数 }; void test01() { //创建计算器对象 calculate c; c.m_num1 = 10; c.m_num2 = 10; cout << c.m_num1 << "+" << c.m_num2 << "=" << c.getresult("+") << endl; cout << c.m_num1 << "-" << c.m_num2 << "=" << c.getresult("-") << endl; cout << c.m_num1 << "*" << c.m_num2 << "=" << c.getresult("*") << endl; } // 多态实现计算器!!!!!!!!! // 好处: 1 组织结构清晰 // 2 可读性强 // 3 对于前期和后期扩展以及维护性高 //实现计算器的抽象类 class abstractclaculator { public: virtual int getresult() { return 0; } int m_num1; int m_num2; }; //加法计算器类 class addcalculator : public abstractclaculator { public: int getresult() { return m_num1 + m_num2; } }; //减法计算器类 class subcalculator : public abstractclaculator { public: int getresult() { return m_num1 - m_num2; } }; //加法计算器类 class mulcalculator : public abstractclaculator { public: int getresult() { return m_num1 * m_num2; } }; void test02() { //多态使用条件 //父类指针或者引用指向子类对象 //加法运算 abstractclaculator* abc = new addcalculator; //new一个对象 abc->m_num1 = 100; abc->m_num2 = 100; cout << abc->m_num1 << "+" << abc->m_num2 << "=" << abc->getresult() << endl; //new的堆区数据,手动销毁,堆区数据,指针类型没变 delete abc; //减法运算 abc = new subcalculator; //new一个对象 abc->m_num1 = 100; abc->m_num2 = 100; cout << abc->m_num1 << "-" << abc->m_num2 << "=" << abc->getresult() << endl; delete abc; //乘法运算 abc = new mulcalculator; //new一个对象 abc->m_num1 = 100; abc->m_num2 = 100; cout << abc->m_num1 << "*" << abc->m_num2 << "=" << abc->getresult() << endl; delete abc; } int main() { test02(); system("pause"); return 0; } 在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容
因此可以将虚函数改为纯虚函数
纯虚函数语法:virtual返回值类型函数名(参数列表)= 0 ;
当类中有了纯虚函数,这个类也称为抽象类
抽象类特点:
1 无法实例化对象。2 子类必须重写抽象类中的纯虚函数,否则也属于抽象类#include7.4 多态案例二-制作饮品using namespace std; // class base { public: //纯虚函数 //只要有一个纯虚函数,这个类就成为抽象类 //抽象类特点: //1. 无法实例化对象 //2. 抽象类的子类 必须要重写父类中的纯虚函数 否则也属于抽象类 virtual void func() = 0; }; class son :public base { public: virtual void func() { cout <<"func函数的调用" << endl; } }; void test01() { //base b; //战区,不允许 //new base; //堆区也不可以 //son s;//子类必须重写父类的纯虚函数,否则无法实例化对象 base* base = new son; base->func(); } int main() { test01(); system("pause"); return 0; }
创建制作流程的虚函数,然后制作咖啡和制作茶叶分别对过程进行改造,输出即可。#include7.5 虚析构和纯虚析构using namespace std; // class abstractdrinking { public: //煮水 virtual void boil() = 0; //冲泡 virtual void brew() = 0; //倒入杯子中 virtual void pourincup() = 0; //加入辅助作料 virtual void putsomething() = 0; //制作饮品 void makedrink() { boil(); brew(); pourincup(); putsomething(); } }; //制作咖啡 class coffee : public abstractdrinking { //煮水 virtual void boil() { cout << "煮农夫山泉" << endl; } //冲泡 virtual void brew() { cout << "冲泡咖啡" << endl; } //倒入杯子中 virtual void pourincup() { cout << "倒入杯中" << endl; } //加入辅助作料 virtual void putsomething() { cout << "加入糖和牛奶" << endl; } }; //制作茶叶 class tea : public abstractdrinking { //煮水 virtual void boil() { cout << "煮自来水" << endl; } //冲泡 virtual void brew() { cout << "冲泡茶叶" << endl; } //倒入杯子中 virtual void pourincup() { cout << "倒入玻璃杯中" << endl; } //加入辅助作料 virtual void putsomething() { cout << "加入柠檬,枸杞" << endl; } }; //制作函数 void dowork(abstractdrinking * abs) //abstractdrinking * abs = new coffee 父类的指针指向子类的对象 { abs->makedrink(); delete abs; //手动开辟,手动释放 } void test01() { //制作咖啡 dowork(new coffee); //制作茶叶 cout << "_______________________茶叶制作" << endl; dowork(new tea); } int main() { test01(); system("pause"); return 0; } 多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方式:将父类中的析构函数改为虚析构或者纯虚析构
虚析构和纯虚析构共性: 可以解决父类指针释放子类对象·都需要有具体的函数实现
虚析构和纯虚析构区别: 如果是纯虚析构,该类属于抽象类,无法实例化对象#include7.6 多态案例三-电脑组装using namespace std; // class animal { public: animal() { cout << "animal构造函数调用" << endl; } //利用虚析构可以解决 父类指针释放子类对象时不干净的问题 // virtual ~animal() // { // cout << "animal析构函数调用" << endl; // } //纯虚析构,需要声明也需要函数的实现。 //有了纯虚析构之后,这个类也属于抽象类,无法实例化对象 virtual ~animal() = 0; //纯虚函数 virtual void speak() = 0; }; animal:: ~animal() { cout << "animal纯虚析构函数调用" << endl; } class cat : public animal { public: cat(string name) { cout << "cat构造函数调用" << endl; m_name = new string(name); } void speak() { cout <<*m_name<< "小猫在说话" << endl; } ~cat() { if (m_name != NULL) { cout<<"cat析构函数调用" << endl; delete m_name; m_name = NULL; } } string* m_name; }; void test01() { animal* animal = new cat("tom"); animal->speak(); //父类指针在西沟时候 不会调用子类的析构函数,导致子类如果有堆区属性,会出现内存泄漏 delete animal; } int main() { test01(); //总结: // 1.虚析构或纯虚析构就是用来解决通过父类指针释放子类对象 // 2.如果子类中没有堆区数据,可以不写为虚析构或纯虚析构 // 3.拥有纯虚析构函数的类也属于抽象类 system("pause"); return 0; } 电脑主要组成部件为CPU (用于计算),显卡(用于显示),内存条(用于存储)
将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenovo厂商
创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口
测试时组装三台不同的电脑进行工作#include8 文件 *** 作 8.1文本文件,写文件using namespace std; //抽象不同零件类 //抽象cpu类 class CPU { public: //抽象计算函数 virtual void calculate() = 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) //接收指针 { m_cpu = cpu; m_mem = mem; m_vc = vc; } //提供工作的指针 void work() { //让零件工作起来,调用接口 m_cpu->calculate(); m_vc->display(); m_mem->storage(); delete m_cpu, m_mem, m_vc; } private: CPU* m_cpu; //CPU的零件指针 Videocard* m_vc; Memory* m_mem; }; //具体厂商 //Intel厂商 class Intelcpu : public CPU { public: void calculate() { 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 calculate() { 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* intercpu = new Intelcpu; Videocard * intelcard = new Intelvideocard; Memory* intelmem = new Intelmemory; //创建第一台电脑 Computer* computer1 = new Computer(intercpu, intelcard, intelmem); computer1->work(); delete computer1; //第二台电脑零件 CPU* lenovocpu = new Lenovocpu; Videocard* lenovocard = new Lenovovideocard; Memory* lenovomem = new Lenovomemory; cout << "第二台电脑——————————————————————————" << endl; //第二台电脑组装 Computer* computer2 = new Computer(lenovocpu, lenovocard, lenovomem); computer2->work(); delete computer2; cout << "第三台电脑——————————————————————————" << endl; //第三台电脑组装 Computer* computer3 = new Computer(new Lenovocpu, new Intelvideocard, new Lenovomemory); computer3->work(); delete computer3; } int main() { test01(); system("pause"); return 0; } 程序运行时产生的数据都属于临时数据,程序—旦运行结束都会被释放通过文件可以将数据持久化
C++中对文件 *** 作需要包含头文件< fstream >
文件类型分为两种:
1.文本文件-文件以文本的ASCII码形式存储在计算机中
2.二进制文件-文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们
*** 作文件的三大类:1.ofstream:写 *** 作2. ifstream:读 *** 作3. fstream :读写 *** 作
#include8.2 文本文件,读文件using namespace std; #include // void test01() { //1. 包含头文件 fstream //2. 创建流对象 ofstream写 *** 作代码 ofstream ofs; //3. 指定打开方式 ofs.open("test.txt", ios::out); //4. 写内容 ofs << "姓名:张三" << endl; //左移运算符向文件写入数据 ofs << "性别:男" << endl; ofs << "年龄:18" << endl; //5. 关闭文件 ofs.close(); } int main() { test01(); system("pause"); return 0; } #includeusing namespace std; #include #include // void test01() { //1. 包含头文件 fstream //2. 创建流对象 ifstream写 *** 作代码 ifstream ifs; //3. 打开文件,并且判断是否打开成功 ifs.open("test.txt", ios::in); if (!ifs.is_open()) { cout<< "文件打开失败" << endl; return; //return后面什么不加表示结束程序 } //4. 读数据 //第一种 char buf[1024] = {0}; //表示定义了一个大小为1024的char数组,并将所有元素赋值为0。 while (ifs >> buf) { cout << buf< 8.3 二进制文件,写文件 以二进制的方式对文件进行读写 *** 作打开方式要指定为ios:binary
二进制方式写文件主要利用流对象调用成员函数write
函数原型:ostream& write(const char * buffer,int len);
参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数#include8.4 二进制文件,读文件using namespace std; #include #include // class person { public: char m_name[64]; int m_age; }; void test01() { //1. 包含头文件 fstream //2. 创建流对象 ofstream ofs; //3. 指定打开方式 ofs.open("person.txt", ios::out | ios::binary); //ios::binary二进制写文件的时候必须要加的 //4. 写内容 person p = { "张三",19 }; ofs.write((const char *)&p, sizeof(person)); //5. 关闭文件 ofs.close(); } int main() { test01(); system("pause"); return 0; } 二进制方式读文件主要利用流对象调用成员函数read
*函数原型:istream& read(char buffer ,int len);
参数解释 l 字符指针buffer指向内存中一段存储空间。len是读写的字节数#includeusing namespace std; #include #include // class person { public: char m_name[64]; int m_age; }; void test01() { //1. 包含头文件 fstream //2. 创建流对象 ifstream ifs; //3. 打开文件。判断文件是否打开成功 ifs.open("person.txt", ios::in | ios::binary); //ios::binary二进制写文件的时候必须要加的 if (!ifs.is_open()) { cout <<"文件打开失败" << endl; return; } //4. 读内容 person p; ifs.read((char*)&p, sizeof(person)); cout <<"姓名" < 欢迎分享,转载请注明来源:内存溢出
评论列表(0条)