当前位置: 首页 > news >正文

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

一、关系运算符重载

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

#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];
	//让用户输入的内容保存到str
	cin >> 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; //20
	cout << Base::m_A; //10

	Son::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; //20
	cout << d.C2::m_A << endl; //20
	//不加作用域不会有二义性
	cout << d.m_A << endl;
}

int main()
{
	test();
	system("pause");
	return 0;
}

相关文章:

  • Docker常用命令 - 黑马学习笔记
  • 抽象⼯⼚模式
  • 基于React Native开发的非法App破解记录
  • 年度征文 | 回顾2022,展望2023(我难忘的2022,我憧憬的2023)
  • JavaScript篇.day08-DOM,节点,事件,定时器,位置及坐标
  • QML教程(七) JavaScript
  • 蓝桥杯寒假集训第四天(全球变暖DFS)
  • VScode中不同目录间python库函数的调用
  • C语言版扫雷——从0到1实现扫雷小游戏
  • 机器学习笔记 - 模式识别之图像特征提取和特征选择的基本方法总结
  • APP应用渗透测试思路
  • 微信小程序框架
  • 网络编程 udp/ip协议 c/s模型
  • 【数据结构】C语言实现链表(单链表部分)
  • JAVA练习8
  • 「前端早读君006」移动开发必备:那些玩转H5的小技巧
  • 【162天】黑马程序员27天视频学习笔记【Day02-上】
  • Elasticsearch 参考指南(升级前重新索引)
  • ES2017异步函数现已正式可用
  • IDEA 插件开发入门教程
  • JavaScript中的对象个人分享
  • node入门
  • 关于List、List?、ListObject的区别
  • 解析带emoji和链接的聊天系统消息
  • 使用 @font-face
  • 智能网联汽车信息安全
  • 智能情侣枕Pillow Talk,倾听彼此的心跳
  • ​DB-Engines 11月数据库排名:PostgreSQL坐稳同期涨幅榜冠军宝座
  • ​如何防止网络攻击?
  • # Panda3d 碰撞检测系统介绍
  • #HarmonyOS:Web组件的使用
  • #ifdef 的技巧用法
  • $forceUpdate()函数
  • $L^p$ 调和函数恒为零
  • (10)STL算法之搜索(二) 二分查找
  • (31)对象的克隆
  • (51单片机)第五章-A/D和D/A工作原理-A/D
  • (70min)字节暑假实习二面(已挂)
  • (9)YOLO-Pose:使用对象关键点相似性损失增强多人姿态估计的增强版YOLO
  • (BFS)hdoj2377-Bus Pass
  • (大众金融)SQL server面试题(1)-总销售量最少的3个型号的车及其总销售量
  • (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
  • (附源码)ssm失物招领系统 毕业设计 182317
  • (官网安装) 基于CentOS 7安装MangoDB和MangoDB Shell
  • (全部习题答案)研究生英语读写教程基础级教师用书PDF|| 研究生英语读写教程提高级教师用书PDF
  • (十六)一篇文章学会Java的常用API
  • (原創) 如何優化ThinkPad X61開機速度? (NB) (ThinkPad) (X61) (OS) (Windows)
  • (转) SpringBoot:使用spring-boot-devtools进行热部署以及不生效的问题解决
  • (转)创业的注意事项
  • .chm格式文件如何阅读
  • .Net 中Partitioner static与dynamic的性能对比
  • .net实现头像缩放截取功能 -----转载自accp教程网
  • .NET中winform传递参数至Url并获得返回值或文件
  • /etc/motd and /etc/issue
  • ??如何把JavaScript脚本中的参数传到java代码段中