(黑马C++)L06 重载与继承

news/2024/12/29 0:43:36/

一、关系运算符重载

以重载等于号运算符为例:

#include<string>
#include <iostream>
using namespace std;class Person {
public:Person(string Name, int age) {this->m_Name = Name;this->m_Age = age;}public:string m_Name;int m_Age;
};bool operator== (Person& p1, Person& p2) {if ((p1.m_Name == p2.m_Name) && (p1.m_Age == p2.m_Age)) {return true;}return false;
}void test() {Person p1("小明", 10);Person p2("小强", 15);Person p3("小强", 15);if (p1 == p2) {cout << "p1和p2相等" << endl;}else {cout << "p1和p2不相等" << endl;}if (p2 == p3) {cout << "p2和p3相等" << endl;}else {cout << "p2和p3不相等" << endl;}}int main()
{test();system("pause");return 0;
}

二、函数调用运算符重载

函数调用运算符就是()。

#include <iostream>
#include <string>
using namespace std;class MyPrint{
public:void operator() (string text) {cout << text << endl;}
};void test() {MyPrint myPrint;myPrint("你好"); //仿函数
}int main(int argc, char** argv)
{test();system("pause");return 0;
}

不要重载&&和||运算符,重载了&&和||符号后短路特性就会失效,按照符号的先后顺序进行运算。

符号重载的总结

  • =,[],()和->操作符只能通过成员函数进行重载
  • <<和>>只能通过全局函数配合友元函数进行重载
  • 不要重载&&和||操作符,因为重载后无法实现短路规则

三、强化训练-字符串封装

1.输出自定义的字符串;2.让用户能任意输入一个字符串。

//MyString.h
#pragma once //防止头文件重复编译
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;class MyString {friend ostream& operator<< (ostream& cout, MyString& str);friend istream& operator>> (istream& cin, MyString& str);
public://有参构造MyString(const char* str);//拷贝构造MyString(const MyString& str);//析构~MyString();private:char* pString; //指向堆区的指针int m_Size; //字符串的大小
};//MyString.cpp
#include "MyString.h"//输出私有属性需要将其作为友元
ostream& operator<< (ostream& cout, MyString& str) {cout << str.pString;return cout;
}//右移运算符的重载
istream& operator>> (istream& cin, MyString& str) {char buf[1024];//让用户输入的内容保存到strcin >> buf;if (str.pString != NULL) {delete[] str.pString;str.pString = NULL;}str.pString = new char[strlen(buf) + 1];strcpy(str.pString, buf);str.m_Size = strlen(buf);return cin;
}MyString::MyString(const char* str)
{cout << "有参构造调用" << endl;this->pString = new char[strlen(str) + 1];strcpy(this->pString, str);this->m_Size = strlen(str);
}MyString::MyString(const MyString& str)
{this->pString = new char[str.m_Size + 1];strcpy(this->pString, str.pString);this->m_Size = str.m_Size;
}MyString::~MyString()
{cout << "析构函数调用" << endl;if (this->pString != NULL) {delete[] this->pString;this->pString = NULL;}
}//字符串的封装.cpp
#include <string>
#include <iostream>
#include "MyString.h"
using namespace std;//测试MyStrring
void test() {MyString str = "abc";cout << str << endl;cout << "请输入str新的内容:" << endl;cin >> str;cout << "您输入的str的内容为:" << str << endl;
}int main()
{test();system("pause");return 0;
}

后续实现cin, cout, =, +, [], == 的重载功能。

四、继承的引出

两个类的成员函数及成员变量高度重复时,如果分别定义会出现很多重复代码,因此,引出继承,一个成员可直接继承另一个成员的属性。

继承的目的是为了减少代码冗余,被继承的类叫做基类(父类),继承的类叫做派生类(子类)。

例如一个新闻类和一个娱乐类的网页,除了中间部分,网页的头部底部侧栏都是共同属性,使用继承的写法如下:

将公共部分提出来写一个单独的BasePage类,新闻页和娱乐页都可以继承BasePage,类中只需要再单独写content的部分。

#include <string>
#include <iostream>
using namespace std;//使用继承写法--重复代码都写到基类网页
class BasePage {
public:void header() {cout << "网页头部" << endl;}void footer() {cout << "网页底部" << endl;}void left() {cout << "网页侧栏" << endl;}
};class News : public BasePage { //继承的写法
public:void content() {cout << "新闻页" << endl;}
};class YULE : public BasePage {
public:void content() {cout << "娱乐页" << endl;}
};void test() {//测试News news;news.footer();YULE yl;yl.content();
}int main()
{test();system("pause");return 0;
}

五、继承方式

  • 继承语法:
class 子类名 : 继承方式 父类名 {//子类新增的数据成员和成员函数
};
  • 三种继承方式:1.public:公有继承 2.private:私有继承 3.protected:保护继承。

保护属性和私有属性只能类内访问,不能类外访问。

六、继承中的对象模型

子类中会继承父类的私有成员,只是被编译器隐藏了起来,所以访问不到。

例如,下面运行sizeof(Son)时会显示16,而不是12。

class Base{
public:int m_A;
protected:int m_B;
private:int m_C;
};class Son : public Base{
public:int m_D;
};

七、继承中的构造与析构

继承类进行对象声明,构造函数和析构函数的调用顺序如下:

  • 父类构造函数调用
  • 子类构造函数调用
  • 子类析构函数调用
  • 父类析构函数调用

子类会继承父类的成员函数和成员属性,但是子类不会继承父类的构造函数和析构函数。

#include <iostream>
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 test() {Son s1;
}int main() {test();system("pause");return 0;
}

如果父类没有默认构造函数,子类可以通过初始化列表的方式显式的调用父类的其他构造。

class Base2{
public:Base2(int a) {this->m_A = a;cout << "有参构造函数的调用" << endl;}int m_A;
}class Son2 : public Base2{
public:Son2(int a) : Base2(a) {}
}

八、继承中的同名成员处理

如果子类和父类拥有同名的函数和属性,如果用子类对象调用就会默认调用子类的函数和属性,调用父类时需要通过作用域进行同名成员的区分:

Son s1;
s1.Base::fun();

如果子类与父类的成员函数名称相同,子类会把父类的所有同名版本(不论参数情况)都隐藏, 想调用父类方法必须加作用域。

注:

  1. 构造函数和析构函数不能被继承;
  2. operator=不能被继承。

九、继承中的静态成员处理

静态对象在类内声明,类外初始化;非静态成员只能用对象调用。

静态成员属性可以被子类继承,如果子类中有同名成员,会把父类的隐藏,静态成员函数同理。

#include <string>
#include <iostream>
using namespace std;class Base {
public:static void func() {cout << "Base的func()函数" << endl;}static void func(int a) {cout << "Base的func(int)函数" << endl;}static int m_A;
};
//静态成员在类内定义,类外初始化
int Base::m_A = 10;class Son : public Base {
public:static void func() {cout << "Son的func()函数" << endl;}static int m_A;
};
int Son::m_A = 20;void test() {cout << Son::m_A; //20cout << Base::m_A; //10Son::func(); //访问子类的静态成员函数Son::Base::func(10); //通过子类访问父类的静态成员函数
}int main()
{test();system("pause");return 0;
}

十、多继承的概念以及问题

可以从一个类继承,也可以同时从多个类继承,继承多个基类就称为多继承,但是从多个类中继承时可能会由于函数、变量等同名问题导致过多的歧义,出现二义性需要使用作用域进行访问。

多继承使用逗号做拼接,语法如下:

class A : public B1, public B2;

十一、菱形继承和虚继承

菱形继承:两个派生类继承同一个基类而又有某个类同时继承这两个派生类,这种继承被称为菱形继承或者钻石继承。

 菱形继承带来的问题:

  1. C1和C2都继承了Base的数据,D继承C1和C2,当D调用函数或数据时,就会产生二义性。
  2. D继承的Base函数和数据继承了两份,但是这些数据只需要一份就可以。

 菱形问题的解决方案:利用虚继承

  1. 在继承时加virtual关键字,此时为虚基类
  2. D的内部结构:vbptr虚基类指针指向虚基类表;通过虚基类表能找到共有的数据的偏移量
#include <string>
#include <iostream>
using namespace std;class Base {
public:int m_A;
};//虚基类
class C1 :virtual public Base{};
//虚基类
class C2 :virtual public Base {};class D :public C1, public C2 {};//菱形继承的解决办法--虚继承
void test() {D d;d.C1::m_A = 10;d.C2::m_A = 20;//使用虚继承之后,只有一份m_A,操作的都是同一份数据cout << d.C1::m_A << endl; //20cout << d.C2::m_A << endl; //20//不加作用域不会有二义性cout << d.m_A << endl;
}int main()
{test();system("pause");return 0;
}

http://www.ppmy.cn/news/9005.html

相关文章

云计算运营—04 FusionSphere OpenStack 6.5方案介绍

FusionSphere OpenStack 6.5方案介绍 OpenStack 系统架构 OpenStack是什么 OpenStack是目前最流行的开源云操作系统&#xff1a; 资源抽象 OpenStack将各类硬件资源&#xff0c;通过虚拟化与软件定义的方式&#xff0c;抽象成资源池 资源分配与负载调度 OpenStack根据管理员…

【Linux编辑神器:vim】

目录 1. vim的基本概念 2. vim的基本操作 3. vim正常模式命令集 4. vim底行模式命令集 5. 简单vim配置 6 总结 什么是Vi/Vim? vi/vim的区别简单点来说&#xff0c;它们都是多模式编辑器&#xff0c;不同的是vim是vi的升级版本&#xff0c;它不仅兼容vi的所有指令&#xff0…

2023年,我觉得拼夕夕值得去

这一年下来&#xff0c;多少大厂梦破碎了&#xff0c;多少人选择被离开和被留下&#xff0c;都日子不那么好过&#xff0c;但其实结合2022年一整年下来&#xff0c;结合拼夕夕的股价表现&#xff0c;人家一年到头开支节流&#xff0c;人家还不断开新站点&#xff0c;晚会还赞助…

消息服务 + Serverless 函数计算如何助力企业降本提效?

作者&#xff1a;柳下 背景介绍 消息队列服务&#xff08;下文均以 Message Service 命名&#xff09;作为云计算 PaaS 领域的基础设施之一&#xff0c;其高并发、削峰填谷的特性愈发受到开发者关注。Message Service 对上承接消息生产者服务的请求&#xff0c;对下连接消费者…

Qt6 中如何使用 qsb

【写在前面】 Qt 5 的图形体系结构非常依赖 OpenGL 作为底层 3D 图形 API。但过去 8 年来随着 Metal 和 Vulkan 的推出&#xff0c;市场发生了巨大变化。现在&#xff0c;Qt 6 加入了大量不同平台的图形 API&#xff0c;以确保用户可以在所有平台上以最高性能运行 Qt。 在 Qt Q…

MySQL高级 SQL优化【插入数据主键优化】

目录 1&#xff1a;SQL优化 1.1&#xff1a;插入数据 1.1.1&#xff1a;insert 1). 优化方案一&#xff08;批量插入数据) 2). 优化方案二&#xff08;手动控制事务&#xff09; 3). 优化方案三 &#xff08;主键顺序插入&#xff0c;性能要高于乱序插入。&#xff09; …

echarts图表演示图表演示

eCharts图表演示 比如说&#xff0c;公司现在接一个项目&#xff0c;这个中信银行&#xff0c;针对之前所有的贷款、大 小客户&#xff0c;要做出来图表系统&#xff08;演示和查看&#xff09;、报表系统&#xff08;用来打印&#xff09;。 郑州的大数据产业局&#xff0c;…

第一章 R语言介绍

1.为何使用R 与起源于贝尔实验室的S语言类似&#xff0c;R也是一种为统计计算和绘图而生的语言和环境&#xff0c;它是一套开源的数据分析解决方案&#xff0c;由一个庞大且活跃的全球性研究型社区维护。但是&#xff0c;市面上也有许多其他流行的统计和制图软件&#xff0c;如…