程序输出题¶
构造函数和析构函数
#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
解析
- 全局对象
globalG在main前构造,count由0变1,输出第一行。 main中autoA构造,count变2,输出第二行。- 第1次循环:输出分隔行,构造局部
autoB(count3),首次执行到静态局部staticS定义,构造staticS(count4),输出块结束行。autoB离开作用域析构,count降为3。 - 第2次循环:输出分隔行,再次构造
autoB(count4),staticS已存在不再构造,输出块结束行。autoB离开作用域析构,count又降回3。 - 循环结束,输出
-----end main。autoA析构,count降为2。 main返回后,静态局部对象staticS析构(count1),全局对象globalG析构(count0)。销毁顺序与构造顺序相反:先静态局部后全局。
#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
- 先构造基类 A:
p->f();- 基类指针指向派生类对象 + 虚函数,触发多态,执行
B::f(),B 的i=64,输出B::f() 64,换行
- 基类指针指向派生类对象 + 虚函数,触发多态,执行
A a;- 构造普通 A 对象:
i=10→ 输出A()10,调用A::f()→i=21,输出A::f() 21
- 构造普通 A 对象:
B b = gen();- 编译器返回值优化(RVO)直接在
b的空间构造 B 对象,构造过程与输出和第 1 步完全一致
- 编译器返回值优化(RVO)直接在
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的拷贝构造函数未在初始化列表中显式调用基类和成员的拷贝构造,导致它们均被默认构造,c中A::i和a.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=3,B构造体x自增,对象最终x=4,p1为A*指向该B对象A *p2 = new A(9);:直接创建A对象,成员x初始化为9B b0 = *p1;:*p1是A对象,触发转换函数生成B(5),该临时B内部x=6,再用临时B拷贝构造b0,b0最终x=7B &r = b0;:r是b0的引用,二者共用同一个对象,r.x等于b0.x为7B b1 = b0;:调用B拷贝构造,以b0.x=7初始化父类,构造体自增,b1最终x=8B b2 = *p2;:*p2是A对象,触发转换函数生成B(10),该临时B内部x=11,再用临时B拷贝构造b2,b2最终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
解析
dynamic_cast会运行时检查实际对象类型,ap指向的是基类A对象,不是B派生类对象,转换失败,返回空指针。static_cast是编译期强制类型转换,不做运行时类型检查,只要继承关系合法就能转换成功,指针非空。ap实际指向B类对象,运行时类型检查通过,转换成功。-
编译期转换合法,指针非空,判定成立。
-
dynamic_cast依赖虚函数表(这里A有虚析构函数满足条件),做运行时类型校验,向下转型不合法时返回空; 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=10fun(pi,pj):T=int*,交换两个指针自身地址,不改 i、j,依旧i=20,j=10