笔记复习
1.继承
在C++中,我们通过函数来实现代码的复用,防止重复造轮子,但是使用函数也有一个缺点1,就是当函数被定义完成之后,它的功能也就确定了,无法被修改,这时候我们引入继承。
C++中的继承是面向对象编程(OOP)的一个重要特性,它允许你从一个已有的类(父类或基类)创建一个新的类(子类)。子类可以继承父类的属性和方法,也可以扩展或修改它们。继承帮助我们实现代码复用,简化程序的设计和维护。
继承的访问权限及对应语法
继承有三种访问控制修饰符:
1.public继承:子类可以访问父类的公有成员和保护成员,无法访问父类的私有成员
class son class : public father class { ... };
2.protected继承:父类的公有成员和保护成员在子类中都变成保护成员,私有成员仍不可访问
class son class : protected father class { ... };
3.private继承:父类的公有成员和保护成员在子类中变成私有成员,私有成员无法访问。
class son class : private father class { ... };
上面的继承结果反应了c++继承时只可向下兼容不可向上兼容的特点,即公有成员可以变为保护成员或私有成员, 保护成员可以变为私有成员,但私有成员却不能变为保护成员,公有成员,保护成员也不能变成私有公有成员。
注:1.当父类中有构造函数时,为了确保父类的成员能够正确初始化,子类必须显式调用父类的构造函数
2.protected和private继承都会把父类的成员在子类中变为对应的成员,但public继承不会
继承代码示例如下:
#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;
}
创建对象时会自动调用构造函数
在我们为一个类创建对象时,会自动调用默认构造函数,构造函数(如果有的话),目的是为了给对象初始化,因此如果构造函数含参,就必须要传参,那么当父类构造函数含参时呢?
#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;
}
通过这个代码我们可以知道当我们创建子类对象时,是优先调用父类的构造函数。
继承的实际应用
下面我们通过一个代码示例来说明继承如何提高代码的复用性:
编辑一个用于编程教学网站的代码:
#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;
}
可以看到这段代码中有很多重复的部分,但如果直接使用函数的话并不方便,因为不同的部分很多,所以我们采用继承
#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;
}
从这个代码示例中我们可以看到,父类的函数是可以被子类的对象调用的,并且当父类中有多个构造函数时,我们可以决定调用哪一个构造函数。并且我们还知道一个父类是可以被多个子类调用的
继承时调用构造函数和析构函数的顺序
前面我们知道,当我们为子类创建对象时,会先调用父类的构造函数,再调用子类的构造函数,那么析构函数呢?
#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;
}
通过这段代码我们知道,当我们为子类创建对象时,我们会先调用父类的构造函数,再调用子类的构造函数,析构函数的调用顺序则与之相反
继承时是否会全部继承父类的成员?
前面我们知道,继承的时候子类不能够访问父类的私有成员,只能够访问公共成员和保护成员,那么这两类成员是否会被继承下来呢?下面我们通过一段代码来演示:
#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类中一共有四个变量,也就是说即使是无法访问的私有成员也会被子类继承下来,只是在子类中无法访问这类成员
当父类和子类成员同名时如何处理?
当子类和父类出现同名成员时,我们应该如何调用我们想要的成员呢?
答:访问子类成员,直接访问即可;访问父类成员,需要加作用域
这个处理方式对于变量,静态成员和函数来说都是适用的
#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
的数据会被共享,而不是分别继承两份。下面是一个虚继承解决菱形继承问题的代码示例:
#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时默认到结尾
例如:
#include <iostream>
#include <string>
using namespace std;int main() {string str = "Hello, world!";//中间有个空格// 从位置 7 开始截取,长度为 5string 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(字符串)
#include <iostream>
#include <string>
using namespace std;int main() {string str = "12345";// 使用 std::stoi 转换字符串为整型int num = stoi(str);cout << "Converted integer: " << num << endl; // 输出:12345return 0;
}
答案
#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;
}