我想说
每天坚持一点点,坚持带来大改变
今天是刷题的第_13天,加油!
一、选择题
A:派生类继承基类的方式有三种:公有继承、保护继承、私有继承。因为继承就是为了使用一部分基类的成员,所以一般情况都是公有继承。√
B:
C:赋值兼容规则:子类对象、子类对象的地址、子类对象的引用可以分别赋值给父类对象、父类的地址、父类的引用,如下图:
D:继承方式有三种:公有继承、保护继承、私有继承
对于基类中的公有成员,不同继承方式下在子类中的权限不同:
继承方式 | 在子类中的权限 |
---|---|
公有继承 | 公有 |
保护继承 | 保护 |
私有继承 | 私有 |
所以,父类的共有成员在子类中是公有、继承还是保护需要看具体的继承方式!
- 以下程序输出什么
如下图分析:
答案选C
A: 如果要对基类的虚函数完成重写,那么必须要保证派生类的虚函数和基类的虚函数具有相同的参数个数和类型。否则如果两个函数名字相同但是参数个数和类型不相同,会构成隐藏!
B:内联函数:在调用位置展开,无函数地址!,虚函数的调用需要去虚表中找虚函数的地址,而对于内联函数直接展开是没有函数地址的,虚表中不会有该函数的地址,所以内联函数不可能是虚函数!
但是要注意: virtual inline void func(){} 这段代码是可以通过编译的!因为inline对于编译器来说就是一个建议,所以很有可能不满足内联的条件而成为一个虚函数!
C: 父类有虚函数,子类不一定必须要完成虚函数的重写。重写是为了达到多态的条件,也可以不写。
D: static成员函数没有this指针,而虚函数的调用需要this指针去调用对象的信息才能去对应的虚表中找到虚函数的地址。所以没有this指针的函数都不可以作为虚函数!
我们知道,创建一个子类对象会先调用父类的构造函数来构造继承自父类的部分,然后再调用子类的构造函数来构造自己的部分。而析构的时候则相反:先调用子类的析构,再自动调用父类析构。
看选项,不管定不定义,编译器都会生成默认的析构函数。所以A、B肯定错误
析构调用:先子类后父类,所以选C
首先区分两个概念:抽象类和虚基类
virtual void func() = 0; //定义一个纯虚函数
有纯虚函数的类称为:抽象类,抽象类不能被实例化!
那么什么是虚基类呢? 虚基类是为了解决菱形继承的时数据的二义性和数据冗余
如果A类有一个_a成员,那么D类就会有一个B继承下来的_a成员和一个C继承下来的_a成员,会有二义性和数据冗余问题!
class A{};
class B: virtual public A {};
class C: virtual public A {};
class D: public B,public C {};
这样,D中的_a就只有一份了,B里面的_a和C里面的_a都是A里的_a,只有一份!
A是B和C的父类,并且还是虚继承,这个时候,A类就称为虚基类!虚基类就是解决菱形继承的数据冗余和二义性问题的!
来看选项
A:声明为纯虚函数的类是抽象类,不能实例化对象 √
B:虚基类和抽象类不是一个概念 ×
C:原选项应为:子类必须实现基类的虚函数。子类也可补充些基类的虚函数 ×
D:纯虚函数也可以有定义:
virtual void func() = 0
{
cout << “hello”<< endl;
}
这样是可以编译通过的!
A: p的值可以修改,但是p指向的内容不可以被修改 ×
B: 64位下指针的大小固定为8字节,sizeof(p)应为8 ×
C: 内联函数和宏都会展开,都不会开辟函数栈帧,所以总体开销是差不多的,题目说inline调用开销显著大于宏 ×
D: 重载是静态编译的时候确定,虚函数是动态绑定 √
故选A
A:多态就是多种形态,同名函数可以实现不同的功能 ✔
B:重载除了函数重载,还有运算符重载 ❌
C:✔
D:重载表现为静态,多态表现为动态 ✔
综上,首先调用构造函数,然后调用两次析构函数(因为拷贝构造并没有给出,所以不会输出任何内容) ,选A
二、编程题
1. 跳石板
思路
这一题涉及到动态规划,看如下分析
代码
#include <iostream>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
//找到约数 存入vector
void getDivNum(int n, vector<int>& v) {
for (int i = 2; i <= sqrt(n); ++i) {
if (n % i == 0) {
v.push_back(i);
//6=2*3 找i对应的另一半
if (n / i != i) {
v.push_back(n / i);
}
}
}
}
int main() {
//输入N M
int N, M;
cin >> N >> M;
vector<int> stepNum;
//初始化为-1
stepNum.resize(M - N + 1,-1); //开辟空间 从N到M
stepNum[0] = 0; //第一个位置初始化为0
for (size_t i = N; i <= M; ++i) {
//如果此处不可到达
if(stepNum[i - N]==-1)
continue;
//找到i的约数存入vector --》 6 = 2 * 3
vector<int> div_num;
getDivNum(i, div_num);
// 对于i的所有约数
for (int j = 0; j < div_num.size(); ++j) {
//如果 i+约数 的位置在范围内
if (i - N + div_num[j] < M - N + 1) {
//如果下一个位置为-1,直接赋值
if (stepNum[i - N + div_num[j]] == -1) {
stepNum[i - N + div_num[j]] = stepNum[i - N] + 1;
} else {
//否则保持最小步数
stepNum[i - N + div_num[j]] = min(stepNum[i - N] + 1, stepNum[i - N + div_num[j]]);
}
}
}
}
//输出最后一个元素的位置!
cout << stepNum[M-N] << endl;
return 0;
}
// 64 位输出请用 printf("%lld")
2. 参数解析
思路
对于读入的一行字符,遍历字符串
如果遇到空格 或者 遇到\n 那么这之前就是一个参数,把参数尾插到vector
注意: 如果遇到 "
,说明其后面的参数中的空格也属于参数的内容,所以遇到"
就把"
入栈,栈不为空的情况下遇到空格都当作参数内部的字符,栈为空的情况遇到空格才作为分隔符
最后输出vector的长度 ,然后遍历vector输出每一个字符串即可
代码
#include<iostream>
#include<string>
#include<stack>
#include<vector>
using namespace std;
int main()
{
string str;
getline(cin,str); //输入一行
stack<char> st;
vector<string> v;
int len = 0;
string cur;
//遍历字符串
for(size_t i = 0;i<=str.size();++i)
{
//如果是空格 或者走到最后一次
if(str[i]==' ' || i==str.size())
{
//如果栈为空:说明该空格隔绝了参数
if(st.empty())
{
v.push_back(cur);
cur.clear(); //清空cur
}
//如果栈不为空,加等
else
{
cur+=str[i];
}
}
//如果不是空格
else
{
//如果是引号 并且栈中没有配对的引号
if(str[i] == '"')
{
if(st.empty())
{
st.push(str[i]);
}
else
{
st.pop(); //配对就消掉
}
}
else // “”不输出
{
cur += str[i];
}
}
}
cout << v.size() << endl;
for(const auto& i : v)
{
cout << i << endl;
}
return 0;
}