Skip to content

程序输出题

构造函数和析构函数
#include <iostream>
using namespace std;
int count;

class myCla {
private:
    char cc;
public:
    myCla(char ch) {
        ++count;
        cc = ch;
        cout<<"constructor:count="<<count<<",ch="<<ch<<endl;
    }
    ~myCla() {
        --count;
        cout<<"destructor:count="<<count<<",cc="<<getcc()<<endl;
    }
    char getcc() {
        return cc;
    }
};

myCla globalG('G');

int main() {
    myCla autoA('A');
    for(int i=1;i<=2;i++) {
        cout<<"-----beging block"<<endl;
        myCla autoB('B');
        static myCla staticS('S');
        cout<<"-----end block"<<endl;
    }
    cout<<"-----end main"<<endl;
    return 0;
}
答案
constructor:count=1,ch=G
constructor:count=2,ch=A
-----beging block
constructor:count=3,ch=B
constructor:count=4,ch=S
-----end block
destructor:count=3,cc=B
-----beging block
constructor:count=4,ch=B
-----end block
destructor:count=3,cc=B
-----end main
destructor:count=2,cc=A
destructor:count=1,cc=S
destructor:count=0,cc=G
解析
  • 全局对象 globalGmain 前构造,count 由0变1,输出第一行。
  • mainautoA 构造,count 变2,输出第二行。
  • 第1次循环:输出分隔行,构造局部 autoBcount 3),首次执行到静态局部 staticS 定义,构造 staticScount 4),输出块结束行。autoB 离开作用域析构,count 降为3。
  • 第2次循环:输出分隔行,再次构造 autoBcount 4),staticS 已存在不再构造,输出块结束行。autoB 离开作用域析构,count 又降回3。
  • 循环结束,输出 -----end mainautoA 析构,count 降为2。
  • main 返回后,静态局部对象 staticS 析构(count 1),全局对象 globalG 析构(count 0)。销毁顺序与构造顺序相反:先静态局部后全局。
#include <iostream>
using namespace std;

class A {
public:
    A() { cout << "A( )" << endl;}
    ~A() {cout << "~A()" << endl;}
};

class B : public A {
public:
    B() { cout << "B( )" << endl;}
    ~B() {cout << "~B()" << endl;}
};

int main() {
    A *ap = new B[2];
    delete ap;
}
答案
A( )
B( )
A( )
B( )
~A()
解析
  • 构造:依次生成 2 组 A()、B()
  • 析构:基类析构非虚且用 delete 释放数组,只调用一次 ~A(),B 析构不执行,内存泄漏
#include <iostream>
using namespace std;

class C1 {
public:
    C1() {
        cout << "$C1()$";
    }
    C1(const C1& a) {
        cout << "$C1(const C1&)$";
    }
    virtual ~C1() {
        cout << "$~C1()$";
    }
};

class C2 : public C1 {
public:
    C2() {
        cout << "$C2()$";
    }
    ~C2() {
        cout << "$~C2()$";
    }
};

int main() {
    C2* pC2 = new C2();
    cout << endl;
    {
        C1 a = *pC2;
        cout << endl;
    }
    C1* pC1 = pC2;
    delete pC1;
    cout << endl;
}
答案
$C1()$$C2()$
$C1(const C1&)$
$~C1()$$~C2()$$~C1()$
解析
  • new C2():派生类构造先执行基类构造,输出 $C1()$$C2()$
  • C1 a = *pC2:发生对象切片,仅调用基类拷贝构造,输出 $C1(const C1&)$
  • 块结束时局部对象a析构,仅调用基类析构,输出 $~C1()$
  • C1* pC1 = pC2;:只是指针赋值,将派生类指针 pC2 隐式转换为基类指针 pC1,让基类指针指向同一个已存在的 C2 对象。这个过程只修改指针变量的值,不会创建新对象,也不会调用任何构造/析构函数,因此不会触发任何打印。
  • delete pC1:基类析构为虚函数,触发多态析构,顺序为先派生类、后基类,输出 $~C2()$$~C1()$

```cpp

include

using namespace std;

class A { int i; public: A():i(10) { cout << "A()" << i << "\t"; f(); } virtual ~A() { cout << "~A()" << "\t"; } virtual void f() { i+=11; cout << "A::f() " << i << "\t"; } void g() { i+=12; cout << "A::g()" << i << "\t"; } };

class B : public A { int i; public: B():i(20) { cout << "B()" << i << "\t"; f(); } ~B() { cout << "~B()" << "\t"; } void f() { i+=22; cout << "B::f()" << i << "\t"; } void g() { i+=12; cout << "B::g()" << i << "\t"; } };

B gen() { return B(); }

int main() { A* p = new B(); p->f(); cout << endl; A a; B b = gen(); a = b; a.f(); cout << endl; b.g(); delete p; return 0; } ```

答案

text A()10 A::f()21 B()20 B::f()42 B::f()64 A()10 A::f()21 A()10 A::f()21 B()20 B::f()42 A::f()32 B::g()54 ~B() ~A() ~B() ~A() ~A()

解析
  • A* p = new B();
    • 先构造基类 A:i 初始化为 10,输出 A()10;构造函数内调用虚函数不触发多态,执行 A::f()i=21,输出 A::f() 21
    • 后构造派生类B:B 的 i 初始化为 20,输出 B() 20;执行 B::f(),B的 i=42,输出 B::f() 42
  • p->f();
    • 基类指针指向派生类对象 + 虚函数,触发多态,执行 B::f(),B 的 i=64,输出 B::f() 64,换行
  • A a;
    • 构造普通 A 对象:i=10 → 输出 A()10,调用 A::f()i=21,输出 A::f() 21
  • B b = gen();
    • 编译器返回值优化(RVO)直接在 b 的空间构造 B 对象,构造过程与输出和第 1 步完全一致
  • a = b;
    • 发生对象切片:仅拷贝 b 中 A 类子对象的 i(值为 21),a 仍是纯 A 类对象,不具备多态属性
  • a.f();
    • 对象实体静态绑定,调用 A::f()i=32,输出 A::f() 32,换行
  • b.g();
    • g() 非虚函数,静态绑定 B 类,B 的 i=54,输出 B::g() 54
  • delete p;
    • 虚析构触发多态析构:先调用 ~B(),再调用 ~A(),依次输出对应内容
  • main函数结束,栈对象逆序析构
    • 先析构 b~B()~A()),后析构 a~A()
拷贝构造函数
#include <iostream>
using namespace std;

class A {
    int i;
public:
    A(int ii=0):i(ii) { cout << "call A(int ii=0).\n"; }
    A(const A& a) {
        i = a.i;
        cout << "call A(const A&).\n";
    }
    void print() const { cout << "A::i = " << i << endl; }
};

class B : public A {
    int i;
    A a;
public:
    B(int ii = 0): i(ii) { cout << "call B(int ii=0).\n"; }
    B(const B& b) {
        i = b.i;
        cout << "call B(const B&).\n";
    }
    void print() const {
        A::print();
        a.print();
        cout << "B::i = " << i << endl;
    }
};

int main() {
    B b(2);
    b.print();
    B c = b;
    c.print();
    return 0;
}
答案
call A(int ii=0).
call A(int ii=0).
call B(int ii=0).
A::i = 0
A::i = 0
B::i = 2
call A(int ii=0).
call A(int ii=0).
call B(const B&).
A::i = 0
A::i = 0
B::i = 2
解析
  • 构造顺序:先基类 A,再成员对象 a(声明顺序),最后 B 自身。
  • 拷贝问题B 的拷贝构造函数未在初始化列表中显式调用基类和成员的拷贝构造,导致它们均被默认构造cA::ia.i 保持为 0,仅 B::i 被拷贝为 2。
#include <iostream>
#include <string>
using namespace std;

class Exception{
public:
    Exception(string name="none"):m_name(name) {
        cout << "Generating an exception object, name is "<<m_name<< endl;
    }
    Exception(const Exception& old_e) {
        m_name = string("ex ") + old_e.m_name;
        cout << "copy an exception object, name is "<<m_name<< endl;
    }
    virtual ~Exception () {
        cout << "destroy an exception object, name is " <<m_name<< endl;
    }
    string GetName() {return m_name;}
protected:
    string m_name;
};

class A {
public:
    A() {
        cout << "A()" << endl;
    }
    int f(int i) {
        if (i>=10) {
            Exception ex_obj1("ex_obj1");
            throw ex_obj1;
        }
        else
            return i;
    }
    ~A() {
        cout << "~A()" << endl;
    }
};

int main() {
    try {
        A a;
        a.f(10);
        A b;
        b.f(10);
    } catch(Exception& m) {
        cout<<"catch exception"<<endl;
    } catch(...) {
        cout<<"catch unknow exception"<<endl;
    }
    return 0;
}
答案
A()                                               // try 块中构造 A a
Generating an exception object, name is ex_obj1   // a.f(10) 内创建局部异常对象
copy an exception object, name is ex ex_obj1      // throw 左值,拷贝构造真正的异常对象
destroy an exception object, name is ex_obj1      // f 退出,局部对象析构
~A()                                              // 栈展开,已构造的 a 析构
catch exception                                   // 异常被 catch(Exception&) 捕获
destroy an exception object, name is ex ex_obj1   // catch 结束,异常对象析构
#include <iostream>
using namespace std;

class B;
class A {
protected:
    int x;
public:
    A(int x = 0): x(x) {}
    operator B();
    int getx() { return x; }
};

class B: public A {
public:
    B(int x = 0): A(x) { this->x++; }
    B(const B &b): A(b.x) { this->x++; }
};

A::operator B() {
    return *new B(x + 1);
}

int main() {
    A *p1 = new B(3);
    A *p2 = new A(9);
    B b0 = *p1;
    B &r = b0;
    B b1 = b0;
    B b2 = *p2;

    cout << p1->getx() << endl;
    cout << p2->getx() << endl;
    cout << b0.getx() << endl;
    cout << r.getx() << endl;
    cout << b1.getx() << endl;
    cout << b2.getx() << endl;
}
答案
4
9
7
7
8
12
解析
  • A *p1 = new B(3);:构造 B,先 A(3) 使 x=3B 构造体 x 自增,对象最终 x=4p1A* 指向该 B 对象
  • A *p2 = new A(9);:直接创建 A 对象,成员 x 初始化为 9
  • B b0 = *p1;*p1A 对象,触发转换函数生成 B(5),该临时 B 内部 x=6,再用临时 B 拷贝构造 b0b0 最终 x=7
  • B &r = b0;rb0 的引用,二者共用同一个对象,r.x 等于 b0.x7
  • B b1 = b0;:调用 B 拷贝构造,以 b0.x=7 初始化父类,构造体自增,b1 最终 x=8
  • B b2 = *p2;*p2A 对象,触发转换函数生成 B(10),该临时 B 内部 x=11,再用临时 B 拷贝构造 b2b2 最终 x=12
类型转换
#include <iostream>
using namespace std;

class A {
public:
    virtual ~A(){}
};

class B : public A {
};

int main() {
    B *bp;
    B b;
    A a1;
    A &a2 = b;
    try {
        bp = dynamic_cast<B *>(&a1);
        if (bp) {
            cout << "Dynamic_cast (1) OK!"<<endl;
        } else {
            cout << "Dynamic_cast (1) Fail!"<<endl;
        }

        bp = dynamic_cast<B *>(&a2);
        if (bp) {
            cout << "Dynamic_cast (2) OK!"<<endl;
        } else {
            cout << "Dynamic_cast (2) Fail!"<<endl;
        }

        B &b1 = dynamic_cast<B &>(a1);
        cout << "Dynamic_cast (3) OK!" <<endl;
    } catch(...){
        cout << "Dynamic_cast (3) Fail!"<<endl;
    }
}
答案
Dynamic_cast (1) Fail!
Dynamic_cast (2) OK!
Dynamic_cast (3) Fail!
解析
  • &a1 指向真正的 A 对象,转 B* 失败,返回 nullptr
  • &a2 指向 B 对象(基类引用),转 B* 成功。
  • a1 不是 B,转 B& 失败 → 抛出 std::bad_cast,被 catch 捕获。
#include <iostream>
using namespace std;

class A {
public:
    virtual ~A(){}
};

class B : public A{};

int main() {
    A a;
    B b;

    A *ap = &a;
    if (dynamic_cast<B *>(ap))
        cout << "OK1" << endl;
    else
        cout << "FAIL" << endl;

    if (static_cast<B *>(ap))
        cout << "OK2" << endl;
    else
        cout << "FAIL" << endl;

    ap = &b;
    if (dynamic_cast<B *>(ap))
        cout << "OK3" << endl;
    else
        cout << "FAIL" << endl;

    if (static_cast<B *>(ap))
        cout << "OK4" << endl;
    else
        cout << "FAIL" << endl;
}
答案
FAIL
OK2
OK3
OK4
解析
  1. dynamic_cast 会运行时检查实际对象类型,ap 指向的是基类 A 对象,不是 B 派生类对象,转换失败,返回空指针。
  2. static_cast 是编译期强制类型转换,不做运行时类型检查,只要继承关系合法就能转换成功,指针非空。
  3. ap 实际指向 B 类对象,运行时类型检查通过,转换成功。
  4. 编译期转换合法,指针非空,判定成立。

  5. dynamic_cast 依赖虚函数表(这里A有虚析构函数满足条件),做运行时类型校验,向下转型不合法时返回空;

  6. static_cast 仅编译期检查继承语法合法性,不校验运行时真实对象类型。
函数重载
#include <iostream>
using namespace std;

class Base {
public:
    virtual int f1(char x) const { return (int)(x); }
    virtual int f2(int x) { return (2*x); }
    virtual int f3(int x) { return (3*x); }
};

class Derived : public Base {
public:
    virtual int f1(char x) { return (int)(-x); }
    virtual int f2(double x) { return (x/2); }
    virtual int f3(int x) { return (x/3); }
};

void print(Base& b) {
    cout << b.f1('a') << "\t" << b.f2(97) << "\t" << b.f3(99) << endl;
}

int main() {
    Base b;
    Derived d;
    print(b);
    print(d);
    return 0;
}
答案
97      194     297
97      194     33
解析
  • f1 与 f2 在派生类中未覆盖(f1 缺 const、f2 参数是 double),故均调用基类版本。
  • f3 被覆盖,故基类对象用基类版(3×99),派生类对象用派生类版(99/3)。
虚继承
#include <iostream>
using namespace std;

class A {
public:
    int t;
    A() {
        t = 1;
        cout << "A()" << t << endl;
    }
};

class B : virtual public A {
public:
    B() {
        t++;
        cout << "B()" << t << endl;
    }
};

class C : virtual public B, virtual public A {
public:
    C() {
        cout << "C()" << endl;
    }
};

int main() {
    C c;
    return 0;
}
答案
A()1
B()2
C()
解析

虚基类只构造一次:无论虚继承路径有多少条,A 只被构造一次,避免成员 t 的多副本和二义性。

拷贝初始化的隐式转换
#include <iostream>
class C {
public:
    explicit C(int) {
        std::cout << "i" << std::endl;
    }
    C(double) {
        std::cout << "d" << std::endl;
    }
};
int main() {
    C c1(7);
    C c2 = 7;
}
答案
i
d
解析
  • C c1(7);:直接调用带 int 参数的构造函数(explicit 只禁止拷贝式隐式转换,不禁止直接括号初始化),输出i
  • C c2 = 7;:这是拷贝初始化的隐式转换场景,explicit 修饰的 C(int) 不能被隐式调用,所以 7 会被隐式转成 double,调用 C(double) 构造函数,输出 d
默认参数静态绑定
#include <iostream>

struct A {
    virtual void foo(int a = 1) {
        std::cout << "A" << '\n' << a;
    }
};

struct B : A {
    virtual void foo(int a = 2) {
        std::cout << "B" << '\n' << a;
    }
};

int main () {
    A *a = new B;
    a->foo();
}
答案
B
1
解析

C++中虚函数的函数体运行时动态决议,但默认参数是编译期静态决议,看调用指针/引用的静态类型,而不是实际对象类型。

模板函数
#include <iostream>
using namespace std;

template <typename T>
void fun(T &x, T &y) {
    T temp;
    temp = x;
    x = y;
    y = temp;
}

int main() {
    int i,j;
    int *pi = &i, *pj = &j;
    i = 10;
    j = 20;
    fun(i,j);
    cout << "i = " << i << '\t' << "j = " << j << endl;

    fun(pi, pj);
    cout << "i = " << i << '\t' << "j = " << j << endl;
}
答案
i = 20    j = 10
i = 20    j = 10
解析
  • fun(i,j)T=int,引用交换 i、j 值,结果 i=20,j=10
  • fun(pi,pj)T=int*,交换两个指针自身地址,不改 i、j,依旧 i=20,j=10