[学习笔记]C++历年试题解析(⼀)--判断题
少说话。。
1. 引⽤在声明时必须对其初始化,以绑定某个已经存在的变量(或对象),在该引⽤的⽣命期内,该绑定不能被更改。 (√)
解析:
引⽤在声明的时候,必须告诉程序引⽤的是谁,所以需要在声明的时候对其初始化;⽽在其引⽤的⽣命周期内,引⽤绑定是不能被更改的,这是因为,引⽤的声明的时候,相当于给被引⽤的对象起了个别名,如果你把这个别名叫在别⼈⾝上,那就不对了,⽐如,你把李⽩叫成⼩⽩,你再把⼩⽩这个名号安在赵四头上,这就说不通了嘛。。
补充点知识点:
引⽤的类型和他绑定的对象之间的类型必须是严格匹配的,但是有两种情况除外,⼀是初始化常量引⽤时,允许任意表达式作为初始值,只要该表达式的结果可以转化成引⽤类型就可以了。⼆是可以将常量引⽤绑定到⾮const对象上。
//对第⼀点有如下引⽤⽅式是对的
double a = 1.3;
const int &b = a;
//对第⼆点有如下引⽤⽅式是对的
const double PI = 3.14;
double cc=  999;
const double &p1 = PI;//完全匹配的⽅式
const double &p2 = (12+3);//表达式上
const double &p3 = cc;//⾮常量引⽤
2.指针变量在定义时必须对其初始化,以锁定某个已经存在的⽬标变量(或对象),在该指针变量的⽣命期内,该指向不能被更改。
(×)
解析:
指针变量在定义的时候可以不初始化,这个很明显嘛。在指针变量的声明周期内,你当然可以改指针的指向,不然链表怎么实现。//指针变量只声明不定义
int *p;
知识点补充:
可以定义指针的引⽤,但是由于引⽤不是对象,因此不能有指向⼀个引⽤的指针。
//指针的引⽤
int *p = NULL;
int *&a = p;
//其实就是给指针p起个别名a
3.值返回的函数(如:double sqrt(double);)的调⽤表达式,如:sqrt(2.0))代表⼀个⽆名的临时变量(或对象),⼀般不将其⽤作左
值。 (√)
解析:
值返回函数调⽤返回的是⼀个⽆名变量,其⽣命周期很短,也就是说,当调⽤的表达式执⾏完了之后,这个⽆名变量就销毁了。如果你要把他作为左值的话,那么在下⼀句话中他就销毁了,⽽且你给他赋值也没意义,你⼜不知道他存储在那⾥怎么拿到他的值。。
补充:
函数可以引⽤返回,⽽引⽤返回可以作为左值。⽐如我们重载的<<;运算符,将其作为左值,就可以对其连续赋值(好像没说明⽩,看代码吧)。
指针调用成员函数
//假装我在某个类⾥⾯
friend ostream & operator<<(ostream &out,const Myclass &myclass){
out<<myclass.value;
return out;
}
//假装我在类外
cout<<myclass1<<myclass2;
//就这样连续输出
4. 引⽤返回的函数,可以返回该函数中值传递的形参变量(或对象)。 (×)
解析:
函数返回时引⽤返回,那么⼀定要返回⽐这个函数的⽣命周期要长的变量,否则返回⼀个已经销毁的变量没有意义。这个题中,传值传进来的变量的⽣命周期是跟这个函数⼀起的,显然⽣命周期并没有长于这个函数,因此不能返回。
5. 任何类都有构造函数、复制构造函数、析构函数、赋值运算符函数。 (√)
解析:
任何类都有,有时候我们要⾃⼰定义,但⼀般情况下系统会帮我们⾃动定义好,只是没有显式定义⽽已,但其实他是有的。
6. 有静态数据成员的类,⼀般地应该考虑为其设计复制构造函数、析构函数。 (√)
解析:
这个题是这样的,由于我们课本上学静态数据成员的时候呢,拿学⽣⼈数统计当的例⼦,所以按照⽼师的意思,每次创建⼀个新的学⽣对象,在构造时⼈数要加⼀,也就是静态变量值加⼀,析构的时候⾃然减⼀了。题出的不是很好,说实话。。
7. 将⽤于输出的插⼊运算符函数operator<<;设计成友元函数的根本原因是因为进⾏输出操作时需要访问对象的内部数据成员。 (×)
解析:
⾸先这个函数并不是重载<<;操作符,如果不设计为友元函数,那么这个函数就是⼀个重载函数,我们知道这个重载函数⼀定有⼀个参数是this指针,也就是说左操作数⼀定是this指针,这样的话,我们就⽆
法将要输出的数据输出到cout⾥⾯了。我再仔细解释下,<<;会匹配他左边的值和右边的值作为左操作数和右操作数,然后这两个操作数传参到你重载的函数⾥⾯,由于你的参数有⼀个默认的this指针,因此他会被作为默认的左操作数,也就是说,如果匹配的不是这个类的对象,就报错了。所以你想输出到cout⾥,就必须设置为友元函数。
代码还是上⾯的代码,理解下:
//假装我在某个类⾥⾯
friend ostream & operator<<(ostream &out,const Myclass &myclass){
out<<myclass.value;
return out;
}
//假装我在类外
cout<<myclass1<<myclass2;
/
/就这样连续输出
8. 在C++程序中,操作符new的功能与calloc函数的功能完全⼀样。 (×)
解析:
new是calloc的升级版,显然两者的功能不是完全⼀样的。具体差别为:calloc函数只管动态申请空间,不会管怎么释放,⽽new的对象在delete的时候会调⽤其析构函数释放掉其基本空间的数据。
9.创建⼀个C++字符串对象(如:string str;),则sizeof(str)的值等于str.length()的值。其中成员函数length为返回字符串的长度。 (×)解析:
⾸先str没有定义,只是进⾏的声明,所以sizeof(str)的结果就是8(我测试的是8,在不同编译器下是不同的请注意),也就是对应的string 对象所占的基本空间,不随字符串长度变化⽽改变,⽽str.length()返回的就是字符串实打实的长度了,这个没什么可说的。
#include <iostream>
using namespace std;
int main() {
string a;
cout << sizeof(a)<< endl;
cout<<a.length()<<endl;
return0;
}
//结果
8
10.基类的私有数据成员在派⽣类中是存在的,但不可直接访问,需要⽤从基类继承下来的函数访问。(√)
解析:
基类的私有成员在派⽣类中确实是存在的,派⽣类会从基类中继承下来其私有成员,但是不能直接访问,想要访问他,就需要通过调⽤基类的接⼝函数来访问。
知识点补充:
public、protected、private区别和继承⽅式区别
public:public表明该数据成员、成员函数是对所有⽤户开放的,所有⽤户都可以直接进⾏调⽤
private:private表⽰私有,私有的意思就是除了class⾃⼰之外,任何⼈都不可以直接使⽤。
protected:protected对于⼦⼥、朋友来说,就是public的,可以⾃由使⽤,没有任何限制,⽽对于其他的外部class,protected就变成private。
1. 类的构造函数的函数名与类名相同,可以重载构造函数。 (√)
解析:
类的构造函数名于类名相同,嗯。构造函数是有参数的,可以被重载,对。
知识点补充:
冒号语法
我们在类的构造函数后⾯加上:可以在进⼊函数体之前初始化某些成员。
class B{
public:
B(int n){
b = n;
}
private:
int b;
};
class A{
public:
A(int n):bb(n){
a = n;
}
private:
int a;
B bb;
};
2. 类的析构函数可以被重载。 (×)
解析:
类的析构函数是不能被重载的,因为类的析构函数并没有参数,也就意味着他没有参数表,⽽函数的重载是通过参数表对应得到的,所以没有办法对析构函数进⾏重载。
3. 重载运算符函数不能改变运算符的操作数个数、优先级和结合⽅向。 (√)
解析:
运算符重载函数其运算符的优先级、结合性、操作数个数和语法结构保持不变。
4. 引⽤在声明时必须对其初始化,以绑定某个已经存在的变量(或对象),在该引⽤的⽣命期内,该绑定不能被更改。 (√)
解析:
上⾯解析过了。
5.指针变量在定义时必须对其初始化,以锁定某个已经存在的⽬标变量(或对象),在该指针变量的⽣命期内,该指向不能被更改。(×)
解析:
上⾯解析过了。
6.类的⾮静态成员函数均有⼀个隐含的形式参数this指针常量,⽤于指向调⽤该函数的对象。函数体中不能改变该指针常量的指向(即锁定调⽤该函数的对象)。 (√)
解析:
类的⾮静态成员函数⾥都有this指针常量,都已经说了是指针常量了,着就意味着他的指向在定义的那⼀刻就不能修改了。
知识点补充:
指针常量和常量指针
常量指针:指向常量的指针,例如const int *p = &a,可以改变p的指向,但是指向的必须是常量。
指针常量:就是常指针,例如int * const p = & a ,可以修改p指向的变量的值,但是p的指向改不了。
7.派⽣类继承了基类的所有数据成员,并且在派⽣类的成员函数中都能直接访问基类的访问属性为private的成员。 (×)
解析:
上⾯解析过了,不能直接访问。
8.构造派⽣类对象时,先调⽤基类的构造函数,后执⾏派⽣类的构造函数。析构派⽣类对象时,先调⽤基类的析构函数,后执⾏派⽣类的析构函数。 (×)
解析:
构造派⽣类对象的时候,先调⽤基类构造函数,然后调⽤派⽣类构造函数,这是对的。但是在析构的时候呢,要遵从先构造的后析构,后构造的函数先析构的原则,因此析构顺序应该是相反的。
9.含纯虚函数的类称为抽象类,不能创建抽象类的对象,不能定义抽象类的指针变量,不能声明抽象类的引⽤。 (×)
解析:
有纯虚函数的类是抽象类,抽象类是不能创建对象的。但是我们是可以定义抽象类的指针变量的,当然也可以声明他的引⽤。为什么?
抽象类不能创建对象,这个是个规定。
可以定义抽象类的指针变量,我们知道,创建⼀个对象,就会为对象分配具体的内存空间,但是定义指针变量并不会分配具体的内存空间,⽽是只需要4字节的存储指针变量的空间就可以了,剩下的其实什么也没做。
⽽定义抽象类的引⽤,理解起来就更简单了,只不过是给这个类起了个别名,起个名字⽽已,也没分配具体的内存空间,应该也可以吧,哈哈。
10.引⽤返回的函数可以作左值,也避免了函数值返回时创建与返回类型相同的临时⽆名对象。(√)
解析:
引⽤返回的函数可以作为左值,因为函数返回的是⼀个引⽤,⽽引⽤的对象是可以作为左值的。这样确实避免了⽆名对象这样的尴尬。
1. 创建对象意味着给对象分配内存空间。在对象的基本空间中存放了该对象的⾮静态数据成员。 (√)
解析:
创建对象,也就是实例化,就是分配具体的内存空间,这话没⽑病。对象的基本空间中存放的是该对象的⾮静态数据成员,其他的数据成员都在堆空间或者全局数据区⾥⾯放着。
2.运算符sizeof能够测量对象的基本空间及对象的资源空间所占⽤的内存单元字节数的总和。 (×)
解析:
sizeof测量的是对象的基本空间所占的内存空间,是不能测量其堆空间或者全局数据区资源长度的。
3.对于在某函数内创建的多个局部⾃动对象,当该函数返回⽽引起这些局部⾃动对象销毁时,先创建的对象先析构、后创建的对象后
析构。 (×)
解析:
上⾯讲过了,先创建的对象后析构,后创建的对象先析构。
4.类的所有成员函数都有⼀个隐含传递的形参this指针。 (×)
解析:
类的所有成员函数嘛,很容易想到⼀些特殊的⽐如静态成员函数,静态成员函数没有this,因为静态成员函数可以被类的对象共有,也可以被继承,如果有this,那继承下来的函数的this指向谁呢?⼆义性了。这个时候你可能也会想到友元函数,如果友元函数是其他类的成员函数,那他就算是成员函数了(其他类的),有this指针,这个this指针是指向其他类的,⽽当友元函数是普通函数的时候呢,就没有this指针了。
5.类的友元函数可以是另⼀个类的成员函数。甚⾄另⼀个类的所有成员函数都是本类的友元函数(友类)。 (√)
解析:
这个应该是书上原话吧,应该不需要解析。
6.常量成员函数不能修改对象的数据成员的值。 (√)
解析:
所谓常量成员函数,就是在函数声明后⾯加上const,这个作⽤跟在函数参数表的每个参数前⾯加个const的效果是⼀样的。
#include <iostream>
using namespace std;
class A {
public:
void show(int b,int c) const{
cout<<a<<"-"<<b<<"-"<<c<<endl;
}
A(int n){a = n;}
private:
int a;
};
int main() {
A a(5);
a.show(1,2);
return0;
}