[C++] 经典练习题与解析(二)

    xiaoxiao2025-02-25  20

    reference:http://cppquiz.org/q

    [C++] 经典练习题与解析(一)

    (11)

    #include <iostream> struct A { A() { std::cout << "A"; } }; struct B { B() { std::cout << "B"; } }; class C { public: C() : a(), b() {} private: B b; A a; }; int main() { C(); } 答案: BA

            成员变量的初始化顺序由它声明的顺序决定,而不是由它们在初始化列表中的顺序决定。

    (12)

    #include <iostream> int main() { int a = 0; decltype((a)) b = a; b++; std::cout << a << b; } 答案: 11

            根据C++标准:

            decltype(e)的类型是这样被确定的:         如果e是不在圆括号内的表达式,或者是一个不在圆括号内的类成员,decltype(e)就是e实体的类型。         如果不存在这样的实体,或者说e代表了一组重载的函数,那么这个程序是不合规范的。         如果e是在圆括号内。         如果e是右值,那么decltype(e)就是T&&(T是e的类型)         如果e是左值,那么decltype(e)就是T&(T是e的类型)         否则,decltype(e)就是e的类型。         因为这里a是在圆括号内的,所以它不满足第一种类型,它又被作为左值对待,所以b的类型是int&,而不是int。

    (13)

    #include <iostream> using namespace std; class A { public: A() { cout << "a"; } ~A() { cout << "A"; } }; int i = 1; int main() { label: A a; if (i--) goto label; 答案: aAaA

    关于goto:

            回到过去初始化的本地变量,它的生命周期仅存于跳转过来的时候,而不存在于跳转离开后。

    (14)

    #include <iostream> class A { public: virtual void f() { std::cout << "A"; } }; class B : public A { public: void f() { std::cout << "B"; } }; void g(A a) { a.f(); } int main() { B b; g(b); } 答案: A         g(A a)传入一个类型为A的对象的值,而不是引用或指针。这就意味着A的拷贝构造函数被调用。(这和我们传入的对象类型是B没有关系),我们得到了一个全新的类型为A的对象,这就叫做切片。

    (15)

    According to the C++11 standard, what is the output of this program? #include <iostream> struct X { X() { std::cout << "a"; } X(const X &x) { std::cout << "b"; } const X &operator=(const X &x) { std::cout << "c"; return *this; } }; int main() { X x; X y(x); X z = y; z = x; } 答案: abbc         main()中的第一行X x,是直接定义的,它调用了缺省构造函数。         接下来两行是问题的核心:X y(x)和X z = y,并不是第一个调用拷贝构造函数,第二个调用赋值运算符。区别在于第一个是直接初始化,第二个是拷贝初始化。         C++标准:如果初始化是直接初始化,或者它是通过拷贝初始化,并且(…)中的类型和类的类型一样,那么就考虑使用构造器。         所以这两者都使用了拷贝构造函数。         直到z = x,我们才真正使用赋值运算符。

    (16)

    #include <iostream> using namespace std; struct A {}; struct B {}; template<typename T = A> struct X; template<> struct X<A> { static void f() { cout << 1 << endl; } }; template<> struct X<B> { static void f() { cout << 2 << endl; } }; template< template<typename T = B> class C> void g() { C<>::f(); } int main() { g<X>(); }

    答案:2

            一个模版的模板参数是可以有默认模板参数的。当这样一个缺省参数被实例化后,它们运用于模版的模版参数范围。         在这个例子中,模版的模版参数是C,C的作用范围是函数g(),所以C的缺省参数(T=B)被使用,g()中调用的是C<B>::f()

    (17)

    #include <iostream> struct X { virtual void f() const { std::cout << "X"; } }; struct Y : public X { void f() const { std::cout << "Y"; } }; void print(const X &x) { x.f(); } int main() { X arr[1]; Y y1; arr[0] = y1; print(y1); print(arr[0]); } 答案: YX

            arr是X的数组,而不是X的指针,当一个类型为Y的对象存储在里面时,它被转化为X对象,我们就失去了这个对象的Y的特有部分,这常常被称为切片。

    (18)

    #include <iostream> class A { public: virtual void f() { std::cout << "A"; } }; class B : public A { private: void f() { std::cout << "B"; } }; void g(A &a) { a.f(); } int main() { B b; g(b); } 答案; B         关键在于:哪怕B::f()是私有的,它也被调用了。         C++标准:可访问性是在指针使用表达式类型来决定哪个成员函数被调用的时候检查的,访问的指针在a.f()中,它的表达式类型是A&

    (19)

    #include <iostream> #include <limits> int main() { int i = std::numeric_limits<int>::max(); std::cout << ++i; } 答案: 未定义行为         有符号整数溢出是未定义行为。         如果在表达式计算期间,结果不能在数学上定义或者不在可表达的值范围内,这个行为是未定义的。

    (20)

    #include <iostream> #include <vector> int main() { std::vector<int> v1(1, 2); std::vector<int> v2{ 1, 2 }; std::cout << v1.size() << v2.size(); } 答案: 12

            要回答这个问题,我们需要看一看vector构造器的重载。

            vector(size_type n, const T& value);

            效果:构造了一个有n个值拷贝元素的vector容器。(也就是n个值都是value)

            所以v1中有1个2。

     

            非聚合类(如vector)如果使用了列表初始化,并且它自身也有初始化列表构造器,那么该构造器被使用,参数列表示初始化列表为一个参数。

            所以v2是用括号里的参数初始化的,包含两个参数1和2.

    转载请注明原文地址: https://ju.6miu.com/read-1296649.html
    最新回复(0)