C++考点问答题
实参和形参的区别是什么?
答:形参是“形式化的参数”,相当于函数中的自变量,⽽而实参是形参的初始值, 相当于自变量所取的值。
请指出下列函数哪个有错误
(a) int f() { string s;
// ...
return s; }
(b) f2(int i) { /* ... */ }
(c) int calc(int v1, int v1) /* ... */ }
(d) double square(double x) return x * x;
答:(a)错误在于f()返回类型为int,所以不可以返回string型的s.可改为:
string f() { string s;
// ...
return s; }
(b)错误在于没有返回类型,可改为:
void f2(int i) { /* ... */ }
(c)错误在于函数体的括号缺左半部分。可改为:
int calc(int v1, int v1) {/* ... */ }
(d)错误在于函数体没有使⽤用括号括起来,可改为:
double square(double x) { return x * x; }
说明形参、局部变量及局部静态变量的区别。编写一个函数,同时用到这三种形式。
答: 形参相当于函数的自变量,其作用域为函数声明与定义的范围。 局部变量是在函数体中所声明并定义的变量,其作用域在该函数体内。局部静态变量为在函数体中声明的变量,但其作⽤用域是在此函数体及函数体后⾯面程序段落中,生物周期自它被声明之时起,⾄至整个程序结束终⽌止。
函数⽰示例及变量说明如下:
void func(int intpara) //intpara为形参。
{
int localvar; //localvar为局部变量。
static int localstavar; //localstavar为局部静态变量。
return;
}
假设T是某种类型的名字,说明以下两个函数声明的区别:⼀一个是 void f(T),另⼀一个是void f(&T)。
答:前者说明其只有一个形参,且类型为T.以一个类型为T的实参调⽤用该函数,调⽤用过程不改变实参本⾝身的值。 后者说明其只有⼀个形参,且是一个引⽤用型形参,被引⽤用变量类型为T.以类型为T的实参调⽤用该 函数时,实参在函数作⽤用域内可能被改变。
为下⾯的函数编写函数声明,从给定的名字中推测函数具备的功能。
(a)名为compare的函数,返回布尔值,两个参数都是matrix类的引⽤用。
(b)名为change_value的函数,返回vector<int>的迭代器,有两个参数:⼀个是int,另⼀个是 vector<int>的迭代器。
bool compare(matrix &mat1, matrix &mat2); //该函数功能应是比较两个matrix类的对象的大小。
vector<int>::iterator change_value(int val, vector<int>::iterator iter);//功能应是更动vector中指定位置的值,并返回对应迭代器。
在error_msg函数的第二个版本中包含有Errcode类型的参数,其中循环内的elem是什么类型?
答:elem是const std::string&类型。
编写⼀个函数的声明,使其返回数组的引⽤用并且该数组包含10个string对象。不要使⽤用位置返回类型、decltype或者类型别名。
答:string (&func())[10]
说明在下面的每组声明中第⼆条声明语句是何含义。如果有非法的声明,请指出来。
Int calc(int,int);
答: (a)中第二句非法,它对第一句中的函数进⾏行了重复声明。
(b)中第二名是非法声明,因为它与第一句形参完全相同,返回类型不同;
(c)中第二句合法。
下面的哪个调用是非法的?为什么?哪个调⽤用虽然合法但显然与程序员的初衷不符?为什么? char *init(int ht, int wd = 80, char bckgrnd = ' ‘); (a) init(); (b) init(24,10); (c) init(14, '*');
答: (a)非法,因为第一个形参并⽆无默认初始化参数,因此⽆无法执行该调⽤用。
(b)合法。
(c)虽然合法,但与程序员初衷不符。因为该调⽤用执⾏行时向14被传递为ht,’*’被传递给wd,bckgrnd 被默认初始化为空字符。程序员的期望是wd被传值14.
说明下⾯循环的含义,它对assert的使⽤合理吗? string s; while (cin >> s && s != sought) { } // empty body assert(cin);
答:代码中对assert的使用是不合理的。assert适于检查不能发生的条件,⽽而该句中只要用户有 输入cin便为true,assert就将发生。 将其改为assert(s == sought)更为合理。
已知有第217页对函数f的声明,对于下面的每一个调用列出可⾏函数。其中哪个函数是最佳匹配?如果调⽤用不合法,是因为没有可匹配的函数还是因为调用具有⼆义性? (a) f(2.56, 42) (b) f(42) (c) f(42, 0) (d) f(2.56, 3.14)
答: (a)void f(int,int)与void f(double, double = 3.14)为可行函数。没有最佳匹配,调用不合法,因为 调用具有⼆义性。 (b)void f(int)与void f(double, double = 3.14)为可行函数。前者是最佳匹配。 (c)void f(int,int)与void f(double, double = 3.14)为可行函数。前者为最佳匹配。 (d)void f(int,int)与void f(double, double = 3.14)为可行函数。后者为最佳匹配。
编写函数的声明,令其接受两个int形参并且返回类型也是int;然后声明一个vector对象,令其元素是指向该函数的指针。
Int f(int,int);
Using pF=declatype(f)*;
Vector<pF>vec;
下⾯面哪些句⼦子是合法的?如果有不合法的句⼦子,请说明为什么? (a) const int buf; (b) int cnt = 0; (c) const int sz = cnt; (d) ++cnt; ++sz;
答: (a)不合法,因为const对象⼀一旦创建必须被初始化; (b)合法。 (c)合法; (d)不合法,sz是常量,其值不能改变。
下⾯面的哪些初始化是合法的?请说明原因。 (a) int i = -1, &r = 0; (b) int *const p2 = &i2; (c) const int i = -1, &r = 0; (d) const int *const p3 = &i2; (e) const int *p1 = &i2; (f) const int &const r2; (g) const int i2 = i, &r = i;
答: (a)不合法,r是引⽤用,必须在声明语句中定义它所被绑定的对象; (b)只要前⾯面已经定义i2为int型变量,则该句合法,p2是⼀一个常量指针,它指向i2. (c)合法。 (d)只要i2在该句前已被声明为int类型,则该句合法。 (e)若i2是int类型,则该句合法。 (f)不合法,r2是对⼀一个int类型常量的常量引⽤用,它必须被初始化。 (g)若i已被声明为int类型,则此句合法,否则不合法。
假设已有上⼀个练习中定义的那些变量,则下⾯面的哪些语句是合法 的?请说明原因。 (a) i = ic; (b) p1 = p3; (c) p1 = (d) p3 = (e) p2 = p1; (f) ic = *p3;
答: (a)合法,前提是ic已被正确定义了(上例中ic被声明为int常量,但未被初始化,语句非法。) (b)不合法:p3是指向常量int的常量指针,⽽而p1是普通int类型指针。 (c)不合法,p1是普通int型指针,⽽而ic是int型常量。 (d)不合法,p3是常量指针,它应该在声明时被初始化,此后不能再赋值。 (e)不合法,p2是常量指针,不能通过拷⻉贝的⽅方法对其赋值。 (f)不合法,ic是int类型常量,只能在声明语句中初始化,此后不能通过拷贝完成赋值。
对于下⾯面的这些语句,请说明对象被声明成了顶层const还是底层 const? const int v2 = 0; int v1 = v2; int *p1 = &v1, &r1 = v1; const int *p2 = &v2, *const p3 = &i, &r2 = v2;
答: v2是顶层const(因为它自己是常量); v1不是const; p1指向的v1不是const,它自己也不是const,所以既不是顶层const也不是底层const; r1是v1的引用,而v1不是const,因此r1也不是const; p2指向const int,而它本⾝身不是const,所以它是底层const; p3自己是常量指针,指向常量int数据,因此p3既是顶层const,也是底层const(前提是 i已被定义为int常量); r2是int型常量v2的引用,因此它是底层const.
下面的代码是否合法?如果非法,请设法将其修改正确。 int null = 0, *p = null;
答: 不合法,p是指针,需指向⼀个int型变量的地址。可改为: int null = 0, *p = &null;
如果想定义⼀个含有10个元素的vector对象,所有元素的值都是42, 请列举出三种不同的实现⽅方法。哪种⽅方法更好呢?为什么?
答: 第⼀种方法:vector<int> vec1(10,42); 第⼆种方法:vector<int> vec2{42,42,42,42,42,42,42,42,42,42} 第三种方法:使⽤用如下程序段: vector<int> vec3; for(int i=0; i <=9; ++i) vec3.push_back(42); 第一种⽅方法最好,因为它比较简洁:)
⼆分搜索程序中,为什么用的是mid = beg + (end - beg) / 2,⽽非mid = (beg + end) /2;?
答:因为迭代器没有加法运算。
下列数组中元素的值是什么?
string sa[10]; int ia[10]; int main() { string sa2[10]; int ia2[10]; }
答: sa中有10个空字符串; ia中有10个0; sa2与ia2由于在函数内部,没被初始化时,是未定义的。
相比于vector来说,数组有哪些缺点,请列举一些。
答: 数组不够灵活,它们的维度是固定的,因此要使⽤用数组时事先要仔细设计其维度,特别 是维度不能⼩小于所需。 也是由于维度固定,数组不能通过push_back类函数⾃自由填加元素。
指出下面代码中的索引错误。 constexpr size_t array_size = 10; int ia[array_size]; for (size_t ix = 1; ix <= array_size; ++ix) ia[ix] = ix;
答: 这组语句对一个10维数组进⾏行赋值,它从ia[1]即第二个元素开始赋起,这没有问题。问题在于它的最后一个赋值一句是对ia[10]进⾏行赋值,⽽而这个元素是不存在的。
根据4.12节中的标表,在下述表达式的合理位置添加括号,使得添加 括号后运算对象的组合顺序与添加括号前⼀一致。
(a) *vec.begin() (b) *vec.begin() + 1
答: (a)*(vec.begin()); (b)(*(vec.begin()))+1;
C++语言没有明确规定大多数二元运算符的求值顺序,给编译器优化 留下了余地。这种策略实际上是在代码⽣生成效率和程序潜在缺陷之间进⾏行了权衡, 你认为这可以接受吗?请说出你的理由。
答: 这是可以接受的。当C++中采⽤用这种策略时,编译器可根据需要来即时抉 择求值顺序,从⽽而实现尽可能⾼高的执⾏行效率,程序员若避免写作次序影响 求值结果的代码,可规避程序的潜在执⾏行缺陷。 与C++不同,JAVA中采取确定执⾏行次序的策略,此时牺牲了可能的⾼高效, 但获得了程序结构的清晰与明确。 两者所追求的⺫⽬目标不同,因此所采取的策略也不同。
解释在下面的if语句中条件部分的判断过程。
const char *cp = "Hello World"; if (cp && *cp)
答:先计算cp的bool值,由于cp是⾮非空指针,因此它所对应的bool值为 true,因此再计算*cp的bool值,其亦⾮非0,对应bool值为true,因此条件部 分运算结果为true.
尽管下面的语句合法,但它们实际执行的行为可能和预期并不⼀一样, 为什么?应该如何修改? (a) if (p = getPtr() != 0) (b) if (i = 1024)
答:(a) 赋值运算符优先级较低,因此此句等价于if( p = ( getPtr() != 0)), 作者预 期做的条件判断应是判断p= getPtr()与0是否相同,因此可将此句改为: if ((p = getPtr()) != 0) (b)=是赋值运算符⽽而不是相等运算符,因此此句应改为if (i == 1024)
假设ptr的类型是指向int的指针、vec的类型是vector<int>、ival的类 型是int,说明下⾯面的表达式是何含义?如果有表达式不正确,为什么?应该如何 修改? (a) ptr != 0 && *ptr++ (b) ival++ && ival (c) vec[ival++] <= vec[ival]
答: (a)该式非法。 该式等价于(ptr != 0) && (*(ptr++))该式当ptr不是空指针 且ptr所指元素⾮非0时值为true,否则为false,运算之后将ptr向前移动⼀一个位置,而由于ptr是指向int类型的指针⽆无法进⾏行⾃自增运算,因此该式⾮非法。可做如下修改 : ptr != 0 && (*ptr)++ (b)该式将产⽣生未定义结果,其运算过程中明确的是先计算&&左侧的表达式值, 当其为真时计算右侧的值。此处假设ival为-1,则其所对应的bool值为true,此时 ival先执⾏行左侧的⾃自增计算还是先执⾏行右侧的bool值计算是不确定的,⽽而不同的 运算顺序将影响最后结果的得出。不⿎鼓励使⽤用类似的表达式。(可参考 C++2011标准⽂文档(References⺫⽬目录下The C++ Standard – ANSI ISO-IEC 14882-2011.pdf 中的1.9节)可做如下修改:ival && (ival + 1); (c)该式将产⽣生未定义结果,原因同(b),可改为vec[ival] <= vec[ival +1];
假设iter的类型是vector<string>::iterator,说明下⾯面的表达式是否合 法。如果合法,表达式的含义是什么?如果不合法,错在何处? (a) *iter++; (b) (*iter)++; (c) *iter.empty() (d) iter->empty(); (e) ++*iter; (f) iter++->empty();
答: (a)合法,后置递增运算符比*优先级⾼高,因此该表达式等价于*(iter++),⾸首先计算 iter所指元素值,然后将iter向前移动⼀一个位置,使其指向下⼀一个元素; (b)不合法,*iter是string类型,无法进行++运算。 (c)不合法,因为.的优先级⾼高于解引⽤用运算符,因此先计算iter.empty(),⽽而迭代 器类型没有名为empty的成员函数。 (d)合法,等价于(*iter).empty(),由于*iter是string类型,因此该表达式正确。 (e)不合法,*iter为string类型,不适合使⽤用++运算。 (f)合法,先进行(*iter).empty运算,再将iter向后移动一个位置。
在本节关于测验成绩的例子中,如果使用unsigned int作为quiz1的类型会发生什么情况?
答:若所用系统中int使⽤用16位存储,则⽆无法容纳全班同学的成绩数据,不能满足表达需要。
下列表达式的结果是什么?
unsigned long ul1 = 3, ul2 = 7; (a) ul1 & ul2 (b) ul1 | ul2 (c) ul1 && ul2 (d) ul1 || ul2
答:(a)3 (b)7 (c)true (d)true
推断下面代码的输出结果并说明理由。实际运行这段程序,结果和你 想象的⼀样吗?如果不⼀样,为什么? int x[10]; int *p = x; cout << sizeof(x)/sizeof(*x) << endl; cout << sizeof(p)/sizeof(*p) << endl;
答:此处sizeof(x)应为数组x所占空间的大小,sizeof(*x)为其⾸首元素所占空间的大小,因此两者相除,返回数组中元素个数,即第⼀句应输出10. sizeof(p)为整形指针所占空间的大小(int的地址以long型进行存储,在本人所用系统中该值为 64),*p指向数组x的⾸首个元素,sizeof(*p)为整形数值所占空间⼤大⼩小,为32位,因此第二句输出为2.
解释下面这个循环的含义。
constexpr int size = 5; int ia[size] = {1,2,3,4,5}; for (int *ptr = ia, ix = 0; ix != size && ptr != ia+size; ++ix, ++ptr) { /* . . . */ }
答:该循环的含义为:自ptr指向数组首部、下标为0的元素起,只要下标尚在合理范围内(即小于数组size时)且指针还指在数组有效元素时,执行循环体中的内容,每次循环体执⾏行结束时, 将下标加1,指针后移一个元素,继续判断上述循环条件是否为true...
假设有如下的定义,
char cval; int ival; unsigned int ui; float fval; double dval; 请回答在下面的表达式中发⽣生了隐式类型转换吗?如果有,指出来。 (a) cval = 'a' + 3; (b) fval = ui - ival * 1.0; (c) dval = ui * fval; (d) cval = ival + fval + dval;
答:(a)’a’为字符型字⾯面值,需将其转为int型,与3相加后,所得结束为int型,再 将其转为char类型。 (b)ival先转为double类型与1.0相乘,所得结果为double型,将ui转为double型 参与运算,最后所得结果为double类型,将其转为float型。 (c)ui转为float类型,与fval相乘,所得float类型转为double. (d)ival转为float类型与fval相加,将所得结果由float型转为double型与dval相加, 所得结果转为char类型。
用命名的强制类型转换改写下列旧式的转换语句。
int i; double d; const string *ps; char *pc; void *pv; (a) pv = (void*)ps; (b) i = int(*pc); (c) pv = &d; (d) pc = (char*) pv;
答: (a)pv =static_cast<void *> (const_cast<string *>(ps)); (b)i = static_cast<int>(*pc); (c)pv = static_cast<void *>(&d); (d)pc = reinterpret_cast<char *>(pv);
什么是空语句?什么时候会用到空语句?
答:空语句即是只有⼀一个分号的语句。 当程序段落中语法上需要⼀一个语句而语义上(逻辑上)并不需要时可使用空语句。
说明find_char函数中的三个形参为什么是现在的类型,特别说明为什 么s是常量引⽤用而occurs是普通引用?为什么s和occures是引用类型而c不是? 如果令s是普通引用发生什么情况?如果令occurs是常量引用会发生什么情况?
答:使用引用类型可以避免字符串拷备,因此将s设为引用类型。将s设为常量类型,是由于在该 函数作用域内并不需要对s进行改变,设为常量可避免不必要的对s潜在写操作。 c是字符类型,所占存储空间较少,因此不必将其设为引用类型。 occurs设为引用类型的原因时,希望可以通过它来返回不在return语句内的信息。而它在在函数 作用域内可能会改变,因此需将其设为普通引用。 若s是普通引用则⼀方面该函数⽆无法使用常量字符串实参来进行调用,另⼀方面也带来了在函数体内可能被改变的风险。 如果将occurs设为常量引用,则⽆无法用它返回字符c出现的次数。
在类的定义中对于访问说明符出现的位置和次数有限定吗?如果有 是什么?什么样的成员应该定义在public说明符之后,什么样的成员应该定义 在private说明之后?
答:对访问说明符的位置与次数没有限制。 可以被类外访问到的成员定义在public后。 只能被类内访问的成员定义在private后。
使用class和struct时有区别吗?如果有,是什么?
答:有区别,默认访问权限不同,struct中成员的默认访问权限是 public,class中成员的默认访问权限是private。
封装是何含义?它有什么用处?
答:在类的定义中,封闭即指其中private的部分隐藏了实现细节。 通过封闭可将功能的使⽤用与实现相分离,使得系统可以更好地应⽤用需要的变化
在你的Person类中,你将那些成员声明成public的?哪些声明成 private的?解释你这样做的原因。
答:将构造函数, getName(), getAddress()声明为public的。将name与 address声明为private的。 前三种成员函数需被类外调⽤用,因此需声明为public. 后两个数据成员只准许类内访问,实现封闭,因此需声明为private.
友元在什么时候用?请分别列举出使⽤用友元的利弊。
答:类外需要访问private的成员时需将之设置为本类的友元。 其好处是实现了一定的灵活性,满⾜足了特定的访问需要。 坏处是突破了类的封闭性。增加了维护难度。
什么是类的静态成员?他有何优点?静态成员与普通成员有何区 别? 答:直接与类本⾝身关联⽽而不是与每个对象关联的成员是类的静态成员。 使⽤用静态成员,可以避免各个对象的公共元素被重复定义,当该元素变化 时,也只需在⼀一个位置进⾏行修改。适当地使⽤用静态成员,可以提⾼高程序开 发的效率和可维护性。 静态成员存在于任何对象之外。普通成员存在于每个对象中。 针对静态成员不可以使⽤用this指针来进行调用,针对普通成员却可以。