少说话。。
程序题链接:https://www.cnblogs.com/aoru45/p/9898691.html
14级试题---选择题
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; return 0; } //结果 8 0
10.基类的私有数据成员在派生类中是存在的,但不可直接访问,需要用从基类继承下来的函数访问。(√)
解析:
基类的私有成员在派生类中确实是存在的,派生类会从基类中继承下来其私有成员,但是不能直接访问,想要访问他,就需要通过调用基类的接口函数来访问。
知识点补充:
public、protected、private区别和继承方式区别
public:public表明该数据成员、成员函数是对所有用户开放的,所有用户都可以直接进行调用
private:private表示私有,私有的意思就是除了class自己之外,任何人都不可以直接使用。
protected:protected对于子女、朋友来说,就是public的,可以自由使用,没有任何限制,而对于其他的外部class,protected就变成private。
15级试题---选择题
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.引用返回的函数可以作左值,也避免了函数值返回时创建与返回类型相同的临时无名对象。(√)
解析:
引用返回的函数可以作为左值,因为函数返回的是一个引用,而引用的对象是可以作为左值的。这样确实避免了无名对象这样的尴尬。
16级试题---选择题
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); return 0; } //结果 5-1-2
7.由于前增量、前减量、后增量、后减量运算符皆为单目运算符,故在重载后增量、后减量运算符时需要在其运算符函数声明及定义时添加一个纯形式上的参数int,以示与前增量、前减量运算符函数的区别。 (√)
解析:
前增量前减量、后增量后减量运算符都是的重载运算的区别就在于是否加了形式上的参数int,不加的情况就是前增量运算,如++a,加了就是后增量运算,如a++。
class A{ public: A & oeprator++(){ a++; return &*this; } int operator++(int){ int temp=a; temp++; return temp; } private: int a; }
知识点补充:
前增量前减量运算符和后增量后减量运算符都是单目运算符;前增量和前减量运算符的返回的是引用返回(++a),而后增量和后见谅运算符返回的是值返回,也就是创建临时变量。前面也已经解释了值返回也引用返回的区别,所以到这里就很清楚了。
8.派生类(或称为子类)的成员函数可以直接访问其基类(或成为父类)的所有成员,甚至是基类的私有成员。 (×)
解析:
上面解析过,派生类成员函数无法访问基类private成员,只有掉从基类继承下来的接口函数才能访问。
9.含有纯虚函数的类被称为抽象类,可以创建抽象类的对象。 (×)
解析:
上面解析过,抽象类是不能创建对象的。
10.若某个类可能作为基类,则最好将其析构函数设计成虚函数。 (√)
解析:
一个类如果作为基类,就要考虑多态性了。之所以设计成虚函数,是考虑到了基类兼容派生类,即要析构一个由基类构造的派生类成员的时候,如果不设计成虚函数,就会在析构时候出现二义性,实际上只调用了基类的析构函数而没有调用派生类的虚构函数,这不是我们要的。
知识点补充:
虚函数:“一个接口,多个方法”,这句话概括了虚函数的作用。虚函数实现的作用其实是动态绑定,比如,基类声明show为虚函数,两个派生类继承基类,分别写该继承类的show函数的定义,由于基类会兼容派生类,那么基类调用show函数的时候,不能知道调用的是派生类的哪个show函数,实验后发现调用的其实是基类的show函数,但是基类的show函数我们只声明了但没定义。而虚函数恰恰解决了这个问题,函数生命为虚函数之后,调用show 的时候,会根据其数据类型定态绑定show函数,然后调用。