C++学习日记---第19天

笔记复习

1.继承

在C++中,我们通过函数来实现代码的复用,防止重复造轮子,但是使用函数也有一个缺点1,就是当函数被定义完成之后,它的功能也就确定了,无法被修改,这时候我们引入继承。

C++中的继承是面向对象编程(OOP)的一个重要特性,它允许你从一个已有的类(父类或基类)创建一个新的类(子类)。子类可以继承父类的属性和方法,也可以扩展或修改它们。继承帮助我们实现代码复用,简化程序的设计和维护。

继承的访问权限及对应语法

继承有三种访问控制修饰符:

1.**public继承:**子类可以访问父类的公有成员和保护成员,无法访问父类的私有成员

cpp 复制代码
class son class : public father class { ... };

2.**protected继承:**父类的公有成员和保护成员在子类中都变成保护成员,私有成员仍不可访问

cpp 复制代码
class son class : protected father class { ... };

3.private继承:父类的公有成员和保护成员在子类中变成私有成员,私有成员无法访问。

cpp 复制代码
class son class : private father class { ... };

上面的继承结果反应了c++继承时只可向下兼容不可向上兼容的特点,即公有成员可以变为保护成员或私有成员, 保护成员可以变为私有成员,但私有成员却不能变为保护成员,公有成员,保护成员也不能变成私有公有成员。

注:1.当父类中有构造函数时,为了确保父类的成员能够正确初始化,子类必须显式调用父类的构造函数

2.protected和private继承都会把父类的成员在子类中变为对应的成员,但public继承不会

继承代码示例如下:

cpp 复制代码
#include<iostream>
using namespace std;

class base1 {
public:
	int a;
protected:
	int b;
private:
	int c;
};

class son1 :public base1 {
public:
	void func() {
		a = 10;//父类中的公共权限成员到子类中依然是公共权限
		b = 20;//父类中的保护权限成员,到子类中依然是保护权限
		//c = 10;//父类中的私有权限成员,子类无法访问
	};
};

void test01() {
	son1 s1;
	s1.a = 100;
	s1.b = 100;//protected在类外无法被直接访问
}


int main() {
	test01();

	return 0;
}

创建对象时会自动调用构造函数

在我们为一个类创建对象时,会自动调用默认构造函数,构造函数(如果有的话),目的是为了给对象初始化,因此如果构造函数含参,就必须要传参,那么当父类构造函数含参时呢?

cpp 复制代码
#include <iostream>
using namespace std;

class Base {
public:
    Base(int value) {  // 父类构造函数需要一个参数
        cout << "Base Constructor, Value: " << value << endl;
    }
};

class Derived : public Base {
public:
    Derived(int value) : Base(value) {  // 显式调用父类构造函数
        cout << "Derived Constructor" << endl;
    }
};

int main() {
    Derived derivedObj(10);  // 创建Derived对象时,传递参数
    return 0;
}

通过这个代码我们可以知道当我们创建子类对象时,是优先调用父类的构造函数。


继承的实际应用

下面我们通过一个代码示例来说明继承如何提高代码的复用性:

编辑一个用于编程教学网站的代码:

cpp 复制代码
#include<iostream>
#include <windows.h>
using namespace std;

// 普通实现页面

// java页面
class java
{
public:
	void header()
	{
		cout << "首页、公开课、登录、注册...(公共头部)" << endl;
	}
	void footer()
	{
		cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
	}
	void left() {
		cout << "java、python、c++、...(公共分类列表)" << endl;
	}
	void content()
	{
		cout << "java学科视频" << endl;
	}
};

//python页面
class python
{
public:
	void header()
	{
		cout << "首页、公开课、登录、注册...(公共头部)" << endl;
	}
	void footer()
	{
		cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
	}
	void left() {
		cout << "java、python、c++、...(公共分类列表)" << endl;
	}
	void content()
	{
		cout << "python学科视频" << endl;
	}
};

//c++页面
class cpp
{
public:
	void header()
	{
		cout << "首页、公开课、登录、注册...(公共头部)" << endl;
	}
	void footer()
	{
		cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
	}
	void left() {
		cout << "java、python、c++、...(公共分类列表)" << endl;
	}
	void content()
	{
		cout << "c++学科视频" << endl;
	}
};

void test01()
{
	cout << "java下载视频页面如下:" << endl;
	java ja;
	ja.header();	
	ja.footer();
	ja.left();
	ja.content();
	cout << "----------------------------------" << endl;
	cout << "python下载视频页面如下:" << endl;
	python py;
	py.header();
	py.footer();
	py.left();
	py.content();
	cout << "----------------------------------" << endl;
	cout << "c++下载视频页面如下:" << endl;
	cpp c;
	c.header();
	c.footer();
	c.left();
	c.content();
}

int main() {
	SetConsoleOutputCP(65001);
	test01();
	system("pause");
	return 0;
}

可以看到这段代码中有很多重复的部分,但如果直接使用函数的话并不方便,因为不同的部分很多,所以我们采用继承

cpp 复制代码
#include<iostream>
#include <windows.h>
using namespace std;

class basepage{
public:
	void header()
	{
		cout << "首页、公开课、登录、注册...(公共头部)" << endl;
	}
	void footer()
	{
		cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
	}
	void left() {
		cout << "java、python、c++、...(公共分类列表)" << endl;
	}
};

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 << "c++学科视频" << endl;
	}
};
void test01()
{
	cout << "java下载视频页面如下:" << endl;
	java ja;
	ja.header();	
	ja.footer();
	ja.left();
	ja.content();
	cout << "----------------------------------" << endl;
	cout << "python下载视频页面如下:" << endl;
	python py;
	py.header();
	py.footer();
	py.left();
	py.content();
	cout << "----------------------------------" << endl;
	cout << "c++下载视频页面如下:" << endl;
	cpp c;
	c.header();
	c.footer();
	c.left();
	c.content();
}

int main() {
	SetConsoleOutputCP(65001);
	test01();
	system("pause");
	return 0;
}

从这个代码示例中我们可以看到,父类的函数是可以被子类的对象调用的,并且当父类中有多个构造函数时,我们可以决定调用哪一个构造函数。并且我们还知道一个父类是可以被多个子类调用的


继承时调用构造函数和析构函数的顺序

前面我们知道,当我们为子类创建对象时,会先调用父类的构造函数,再调用子类的构造函数,那么析构函数呢?

cpp 复制代码
#include<iostream>
#include <windows.h>
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() {
	SetConsoleOutputCP(65001);
	test01();
	system("pause");
	return 0;
}

通过这段代码我们知道,当我们为子类创建对象时,我们会先调用父类的构造函数,再调用子类的构造函数,析构函数的调用顺序则与之相反


继承时是否会全部继承父类的成员?

前面我们知道,继承的时候子类不能够访问父类的私有成员,只能够访问公共成员和保护成员,那么这两类成员是否会被继承下来呢?下面我们通过一段代码来演示:

cpp 复制代码
#include<iostream>
using namespace std;

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说明父类中的所有非静态的成员属性都会被子类继承
}

int main() {
	test01();

	return 0;
}

这段代码的运行结果显示son类的内存为16个字节,那么说明son类中一共有四个变量,也就是说即使是无法访问的私有成员也会被子类继承下来,只是在子类中无法访问这类成员


当父类和子类成员同名时如何处理?

当子类和父类出现同名成员时,我们应该如何调用我们想要的成员呢?

答:访问子类成员,直接访问即可;访问父类成员,需要加作用域

这个处理方式对于变量,静态成员和函数来说都是适用的

cpp 复制代码
#include<iostream>
using namespace std;

class base {
public:
	base() {
		a = 100;
	}
	void func() {
		cout << "base作用域下的func调用" << endl;
	}
	void func(int a) {
		cout << "base作用域下的func(int a)调用" << endl;
	}
	int a;
};

class son :public base {
public:
	son() {
		a = 200;
	}
	void func() {
		cout << "子类的func调用" << endl;
	}
	int a;
};
//同名成员属性处理
void test01() {
	son s;
	cout << "a=" << s.a << endl;
	cout << "a=" << s.base::a << endl;
}
//同名成员函数处理
//处理方式与同名成员属性相同
void test02() {
	son s;
	s.func();
	s.base::func();
	s.base::func(10);
}

int main() {
	//test01();
	test02();
	return 0;
}

虚继承解决菱形继承问题

下面我们来讲继承的最后一个模块:菱形继承

在讲解菱形继承之前,我们需要明确两个概念,即虚继承和虚基类

  • 虚继承(virtual inheritance)是用来解决菱形继承中的"重复继承"问题的机制,确保父类的成员在多个子类继承时只会存在一份。
  • 虚基类(virtual base class)是指通过虚继承声明的父类,它的成员被所有继承类共享,而不是被分为多份。

接下来我们讲解菱形继承的概念

animal

/ \

sheep tuo

\ /

sheeptuo

如图所示,这种继承方式就被成为菱形继承或者钻石继承

在菱形继承中,如果两个或多个子类继承自同一个父类,而这些子类又被同一个子类所继承,那么在最后一个子类中就会有父类的多个副本,造成内存浪费。

虚继承通过在继承声明前加上 virtual 关键字来解决这个问题。使用虚继承时,animal 类会被称为虚基类 ,并且只有一个副本。在 sheeptuo 类中,animal 的数据会被共享,而不是分别继承两份。下面是一个虚继承解决菱形继承问题的代码示例:

cpp 复制代码
#include<iostream>
using namespace std;

//动物类
class animal {
public:
	int 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::age = 18;
	st.tuo::age = 28;
	//当菱形继承时,若有两个父类拥有相同的数据,需要加以作用域区分
	cout << "sheep" << st.sheep::age << endl;
	cout << "tuo" << st.tuo::age << endl;
	cout << "st.age" << st.age << endl;
}

int main() {
	test01();

	return 0;
}

总结:当出现菱形继承问题时,我们可以在继承父类的时候在继承方式前面加上virtual关键字,即虚继承。

好题精选

问题描述

给定长度为66的字符串SS。保证SS的前三个字符是ABC,最后三个字符是数字。

判断SS是否是在本次比赛开始之前在AtCoder上举办并结束的比赛的缩写。

这里,字符串TT是"在本次比赛开始之前在AtCoder上举办并结束的比赛的缩写",当且仅当它等于以下348348字符串之一:

ABC001ABC002、......、ABC314ABC315ABC317ABC318、......、ABC348ABC349

注意ABC316不包括在内。

约束条件

  • SS是长度为66的字符串,其中前三个字符是ABC,最后三个字符是数字。

输入

输入以以下格式从标准输入中给出:

复制代码
SS

输出

如果SS是在本次比赛开始之前在AtCoder上举办并结束的比赛的缩写,则输出Yes;否则输出No

样例1

Inputcopy Outputcopy
ABC349 Yes

ABC349是在上周在AtCoder上举办并结束的比赛的缩写。

样例2

Inputcopy Outputcopy
ABC350 No

ABC350是本次比赛,尚未结束。

样例3

Inputcopy Outputcopy
ABC316 No

ABC316不是在AtCoder上举办的比赛。

学习到的点

1.c++中截取字符串片段的方法

字符串.substr(num1,num2)

其中num1表示从哪一个索引开始截取(包括);num2表示截取字符串的长度,不输入num2时默认到结尾

例如:

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

int main() {
    string str = "Hello, world!";//中间有个空格
    
    // 从位置 7 开始截取,长度为 5
    string subStr1 = str.substr(7, 5);
    cout << "Sub-string 1: " << subStr1 << endl;  // 输出 "world"
    
    // 从位置 0 开始截取,直到字符串末尾
    string subStr2 = str.substr(0);
    cout << "Sub-string 2: " << subStr2 << endl;  // 输出 "Hello, world!"
    
    // 从位置 7 开始,截取到字符串末尾
    string subStr3 = str.substr(7);
    cout << "Sub-string 3: " << subStr3 << endl;  // 输出 "world!"
    
    return 0;
}

2.c++中将字符串转化为整型变量的方法

stoi(字符串)

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

int main() {
    string str = "12345";
    
    // 使用 std::stoi 转换字符串为整型
    int num = stoi(str);
    
    cout << "Converted integer: " << num << endl;  // 输出:12345
    
    return 0;
}

答案

cpp 复制代码
#include<iostream>
using namespace std;

string A;

int main() {
    cin >> A;
    
    int num = stoi(A.substr(3));  // 获取后3个数字部分
    
    if (num >= 1 && num <= 315) {
        cout << "Yes";
    } else if (num >= 317 && num <= 349) {
        cout << "Yes";
    } else {
        cout << "No";
    }
    
    return 0;
}
相关推荐
Feliz Da Vida12 分钟前
union find算法 c++
开发语言·c++·算法
-一杯为品-14 分钟前
【Python】Matplotlib基本图表绘制
开发语言·笔记·python·学习·matplotlib
minos.cpp23 分钟前
Rust之抽空学习系列(四)—— 编程通用概念(下)
开发语言·学习·rust
轻口味28 分钟前
掌握DevEco Studio这一功能,高效实现ArkTS与C++胶水代码
c++·harmonyos·arkts
余胜辉31 分钟前
transformer口语化解析
算法
硕风和炜32 分钟前
【LeetCode: 1338. 数组大小减半 + 哈希表 + 贪心】
算法·leetcode·散列表·贪心·哈希表
lin zaixi()1 小时前
洛谷 P10483 小猫爬山 完整题解
数据结构·算法
IT猿手1 小时前
基于Q-Learning的机器人栅格地图路径规划,可以更改地图大小及起始点,可以自定义障碍物,MATLAB代码
人工智能·深度学习·算法·机器学习·matlab·机器人·智能优化算法
MonkeyKing_sunyuhua1 小时前
sklearn 不再维护的问题
python·算法
sanguine__1 小时前
javaScript学习
前端·javascript·学习