说明:
下面的题目,有的是看书的时候,弄懂一个问题后,突然想着要是作为面试题,面试官会怎么问?有的来源于网络。权当总结,备战面试。
题目1:
class A
{
private:
int i;
int j;
};
class B:public A
{
private:
int k;
}
请编写类A和类B的构造函数,复制构造函数,复制构造函数?
我相信大部分同学看到问题,窃喜。这个嘛简单嘛。提笔就来。
#include<iostream>
using namespace std;
class A
{
public:
A()
{
i=0;
j=0;
}
A(int i1,int j1)
{
i=i1;
j=j1;
}
A(const A &a)
{
i=a.i;
j=a.j;
}
A& operator =(const A &a)
{
i=a.i;
j=a.j;
return *this;
}
virtual void print()
{
cout<<"i="<<i<<" j="<<j;
}
private:
int i;
int j;
};
class B:public A
{
public:
B(int i,int j,int k1):A(i,j)
{
k=k1;
}
B(const B& b)
{
k=b.k;
}
B& operator =(const B &b)
{
k=b.k;
return *this;
}
virtual void print()
{
A::print();
cout<<" k="<<k;
}
private:
int k;
};
写个main函数测试一下:
int main()
{
B pc(1,5,6);
pc.print();
cout<<endl;
B pc2(2,2,3);
pc2.print();
cout<<endl;
pc2=pc;
pc2.print();
cout<<endl;
}
运行结果一看(结果有误):
i=1 j=5 k=6
i=2 j=2 k=3
i=2 j=2 k=6
当把自定义的复制构造函数,和赋值构造函数注释掉后,结果是正确的。
i=1 j=5 k=6
i=2 j=2 k=3
i=1 j=5 k=6
为什么会这样?
在自定义复制构造函数和赋值构造函数里面,并没有对继承的成员进行赋值,想当然的认为,会自动调用父类的复制构造函数或赋值构造函数。其实这就是effective c++条例12,复制对象时勿忘其每一部分。
正确的做法
在子类的自定义赋值或者复制函数里面,调用基类的赋值或者复制函数,实现基类成员的赋值。
B(const B& b):A(b)
{
k=b.k;
}
B& operator =(const B &b)
{
A::operator=(b);
k=b.k;
return *this;
}
题目2:
const char *p 与char * const q的区别?
答:
const位于星号的左边,则const修饰指针所指向的变量,即指针指向的是常量。
const位于星号的右边,则const修饰指针,即指针本身是个常量。
题目3:
空类大小为多少?
答:1
题目4:
什么时候需要自定义复制构造函数等?
答: 编译器生成的复制构造函数无法满足要求时,需要自定义复制构造函数。最简单的,但成员变量里面有指针。
题目5:
智能指针的实现?
题目6:
string *str,int *pi,vector<int> *ivec,指针大小是否相同?
答:指针大小相同,指针寻址出来的object类型不同,是因为指针类型教导编译器如何解释某个特定地址的内存内容和大小。
题目7:
c++基类构造顺序是以基类声明顺序为顺序。
题目8:
问:用户自定义类A,并且自定一个函数 A test()函数,请问函数写法1,和函数写法2,有没有区别?
写法1:
A test()
{
return A();
}
写法2:
A test()
{
A a;
return a;
}
答案:有区别的。
写法1的:编译器直接把临时变量创建并初始化在外部存储单元,省去了拷贝与析构步骤。
写法2:首先,temp 对象被创建,同时完成初始化;然后拷贝构造函数把temp 拷贝到保存返回值的外部存储单元中;最后,temp 在函数结束时被销毁(调用析构函数)
为了检验这句话,先实现类A,并标记构造函数,复制构造函数等。
#include <iostream>
using namespace std;
class A
{
public:
A(int i=0):num(i)
{
cout << "构造函数 " << num << endl;
}
A(const A &a)
{
num = a.num;
cout << "复制构造函数 " << num << endl;
}
A& operator=(const A &a)
{
cout << "赋值构造函数 " << num << "赋值为" << a.num <<endl;
num = a.num;
return *this;
}
~A()
{
cout << "析构函数 " << num << endl;
}
private:
int num;
};
A test(int i)
{
A a(i);
return a;
}
A test2(int i)
{
return A(i);// 这个时候,编译器直接把临时变量初始化,并创建在外部存储单元
}
下面是测试:
标号 | 调用代码 | 输出结果 |
1 | A a = test(1); | 构造函数 1 复制构造函数 1 析构函数 1 析构函数 1 |
2 | A a = test2(1); | 构造函数 1 析构函数 1 |
3 | A a(3); a = test(1); | 构造函数 3 构造函数 1 复制构造函数 1 析构函数 1 赋值构造函数 3赋值为1 析构函数 1 析构函数 1 |
4 | A a(3); a = test2(1); | 构造函数 3 构造函数 1 赋值构造函数 3赋值为1 析构函数 1 析构函数 1 |
题目9:
下面代码执行结果:
#include <iostream>
using namespace std;
class Base
{
public:
virtual void f()
{
cout<<"Base::f()"<<endl;
}
void d()
{
cout<<"Base::d()"<<endl;
}
};
class Derived:public Base
{
public:
void f()//覆盖基类f()
{
cout<<"Derived::f()"<<endl;
}
void d()//隐藏基类的d()。注意,覆盖!=隐藏
{
cout<<"Derived::d()"<<endl;
}
};
int main()
{
Derived d;
Base *pb=&d;
Derived *pd=&d;
pb->f();
pd->f();
pb->d();
pd->d();
}
答案:
Derived::f()
Derived::f()
Base::d()
Derived::d()
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。