笔记复习
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字符串之一:
ABC001
、ABC002
、......、ABC314
、ABC315
、ABC317
、ABC318
、......、ABC348
、ABC349
。
注意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;
}