总结1
1. 多态类中的虚函数表是Compile-Time,还是Run-Time时建立的?
答:虚拟函数表是在编译期就建立了,各个虚拟函数这时被组织成了一个虚拟函数的入口地址的数组。
2.一个父类写了一个virtual 函数,如果子类覆盖它的函数不加virtual ,也能实现多态?
答:只要基类在定义成员函数时已经声明了virtual关键字,在派生类实现的时候覆盖该函数时,virtual关键字可加可不加,不影响多态的实现。
3. 子类的空间里,有没有父类的virtual函数,或者父类的私有变量。
答:有。除了static的,其它父类的成员变量都有。
4. 类ClassB从ClassA派生,那么ClassA *a = new ClassB(…); 试问该表达是否合法?为什么?
答:合法。因为ClassB继承自ClassA,那么ClassB也可以认为是一种ClassA,因此ClassA的指针可以指向它。
5. 接上。如果ClassA中定义并实现虚函数int func(void),ClassB中也实现该函数,那么上述变量a->func()将调用哪个类里面的函数?如果int func(void)不是虚函数,情况又如何?为什么?
答:如果是虚函数,则调用ClassB中的实现;如果不是虚函数,则调用ClassA中的实现,因为在非虚函数的情况下,ClassA的指针只能看到ClassA的实现。
6. C++函数中值的传递方式有哪几种?
答:值传递;指针传递(也算是值传递吧,指针也是值);引用传递。
7. C++里面是不是所有的动作都是main()引起的?如果不是,请举例。
答:不是。比如全局变量的初始化。
8. C++里面如何声明const void f(void)函数为C程序中的库函数。
答:首先这个题目有点奇怪。1是表达的意思很奇怪;2是const void 返回值很奇怪。
不过这里的意思应该是使用extern "C"这个链接指示来调用其它语言编译的函数:
extern "C" {
const void f(void);
}
9. 下列哪两个是等同的。
int b;
A const int* a = &b; //常量指针
B const* int a = &b; //没有这种写法
C const int* const a = &b; //指向常量的常量指针
D int const* const a = &b; //指向常量的常量指针
答:CD等同。
10. 请讲一讲析构函数和虚函数的用法和作用?
答:析构函数是特殊的类成员函数,它没有返回类型,没有参数,不能随意调用,也没有重载,只有在类对象的生命期结束的时候,由系统自动调用。 有释放内存空间的作用。虚函数是C++多态的一种表现, 使用虚函数,我们可以灵活地进行动态绑定,当然是以一定的开销为代价。
11. 多态。overload 和 override的区别。
答:overload是重载,重载是一种参数多态机制,即代码通过参数的类型或个数不同而实现的多态机制,是一种静态的绑定机制(在编译时已经知道具体执行的是哪个代码段);override是覆盖。覆盖是一种动态绑定的多态机制。即在父类和子类中同名元素(如成员函数)有不同的实现代码,执行的是哪个代码是根据运行时实际情况而定的。
12. “new”in c++ is a:
A. library function like malloc in c B. key word C. operator D. none of the above
答:C. 操作符。
13. 以下代码有什么问题?
答:参见具体代码:
#include <iostream>
using std::cout;
using std::endl;
struct Test
{
Test(int) {}
Test() {}
void fun() {}
};
void main(void)
{
Test a(3);
a.fun();
//Test b(); //定义错误,不需要()
Test b;
b.fun();
}
14. 以下代码有什么问题:
cout << (true?1:"1") << endl;
答:?:中:两边应该为同类型。
15. 下面代码的执行结果是什么:
#include <iostream>
using std::cout;
using std::endl;
struct CLS
{
int m_i;
CLS(int i) : m_i(i) {
cout << "CLS(int i)" << endl;
}
CLS() {
CLS(0);
}
};
void main(void)
{
CLS *obj = new CLS();
cout << obj->m_i << endl;
}
答:会打印CLS(int i);但是打印的m_i的结果不确定。
因为在默认构造函数内部再调用带参的构造函数属用户行为而非编译器行为,亦即仅执行函数调用,而不会执行其后的初始化表达式。只有在生成对象时,初始化表达式才会随相应的构造函数一起调用。
16. C++中的空类,默认产生哪些类成员函数?
答:
默认构造函数
析构函数
拷贝构造函数
赋值运算符(operator=)
取址运算符(operator&)(一对,一个非const的,一个const的)
下面的例子中,即使去掉CLS中的实现,也可以正常运行。这至少能够说明这些实现已经有默认的了:
#include <iostream>
using std::cout;
using std::endl;
class CLS
{
public:
CLS() {
cout << "default construct" << endl;
}
CLS(CLS &c) {
cout << "copy construct" << endl;
}
CLS& operator=(const CLS &c) {
cout << "operator =" << endl;
return *this;
}
CLS* operator&() {
cout << "operator &" << endl;
return this;
}
const CLS* operator&() const {
cout << "cosnt operator &" << endl;
return this;
}
~CLS() {
cout << "destruct" << endl;
}
};
void main(void)
{
//调用默认构造函数
CLS c1, c3;
const CLS c4;
//调用拷贝构造函数
CLS c2 = c1;
//调用赋值运算符
c3 = c1;
//调用取值运算符
&c1;
&c4;
return;
//最后会调用析构函数
}
上述代码的运行结果如下:
default construct
default construct
default construct
copy construct
operator =
operator &
cosnt operator &
destruct
destruct
destruct
destruct
17. 析构函数是否必须为虚函数?为何?
答:析构函数可以为虚函数,也可以不为虚函数。(更多的时候不为虚函数)
设计析构函数为虚函数,主要是考虑到继承。
当A为基类,B为A的继承类,考虑如下情况:
A *p = new B();
.....
delete p;
如果此时A的析构函数不是虚函数,那么在delete p的时候就会调用A的析构函数,而不会调用B的析构函数,这样就会造成B的资源没有释放。而如果A的析构函数为虚函数,那么就会调用B的析构函数,一切正常。
另外,构造函数不能是虚函数。
18. 构造函数和析构函数是否可以被重载,为什么?
答:构造函数可以被重载,但是析构函数不可以。因为构造函数可以带参数,而析构函数不能。
19. 对于一个频繁使用的短小函数,在C语言中应用什么实现,在C++中应用什么实现?
答:c语言用宏,c++用inline函数。
20. 什么叫做多态性?在C++中是如何实现多态的?
答:多态是指同样的消息被不同类型的对象接收时导致完全不同的行为,是对类的特定成员函数的再抽象。C++支持的多态有多种类型,重载(包括函数重载和运算符重载)和虚函数是其中主要的方式。
21. 什么叫做抽象类? 抽象类有何作用?抽象类的派生类是否一定要给出纯虚函数的实现?
答:带有纯虚函数的类是抽象类。抽象类的主要作用是通过它为一个类族建立一个公共的接口,使它们能够更有效地发挥多态特性。抽象类声明了一组派生类共同操作接口的通用语义,而接口的完整实现,即纯虚函数的函数体,要由派生类自己给出。但抽象类的派生类并非一定要给出纯虚函数的实现,如果派生类没有给出纯虚函数的实现,这个派生类仍然是一个抽象类。
22. 列出两个情况是必须使用成员初始化列表,而不在构造函数里面赋值。
答:成员是const,或者是引用,或者是没有默认构造函数的类类型。(需要注意,放在初始化列表中的才是初始化,而在构造函数中的其实是赋值,而像const之类的不能再赋值了。)
23. static_cast和dynamic_cast有什么区别?
答:static_cast就是静态转换,它近似于c语言中的强制转换,可以用于内置类型,也可以用于类类型,但是安全性只能由写代码的人自己保证。
dynamic_cast是动态转换,它只要应用于具有继承关系的类之中,它有更多的安全方面的验证,比如需要转换后的类型是引用或者指针,需要保证从基类到派生类的转换中,基类的多态性。另外,,dynamic_cast可以识别出不安全的转换,但并不抛出异常,而是将转换的结果设置成null。
24. namespace解决了什么问题?
答:可以防止命名冲突。
25. 以下程序的打印结果是什么?
#include <iostream>
using std::cout;
using std::endl;
class A {
protected:
int data;
public:
A(int data = 0) {
cout << "A constructor" << endl;
this->data = data;
}
int GetData() {
return doGetData();
}
virtual int doGetData() {
return this->data;
}
};
class B : public A {
protected:
int data;
public:
B(int data = 1) {
cout << "B constructor" << endl;
this->data = data;
}
int doGetData() {
return this->data;
}
};
class C : public B {
protected:
int data;
public:
C(int data = 2) {
cout << "C constructor" << endl;
this->data = data;
}
};
void main(void) {
C c(10);
cout << c.GetData() << endl;
cout << c.A::GetData() << endl;
cout << c.B::GetData() << endl;
cout << c.C::GetData() << endl;
cout << c.doGetData() << endl;
cout << c.A::doGetData() << endl;
cout << c.B::doGetData() << endl;
cout << c.C::doGetData() << endl;
}
答:运行结果如下:
A constructor
B constructor
C constructor
1
1
1
1
1
0
1
1
说明:首先这里考察了一个子类构造函数与父类构造函数的问题,在网上看到的一种说法是,“在创建子类对象时候,如果子类的构造函数没有显式调用父类的构造函数且父类只定义了自己的有参构造函数,则会出错”,从运行的结果来看,这个说法就是有问题的的。至于具体的GetData(),考察的是虚函数的东西,不具体展开了。
另外,还有一个考察的点是继承的父子类之间的同名成员变量之间的关系,下面一题也有说明。
26. 下面的代码有哪些问题?
答:参见具体代码,错误的代码已经注释起来:
#include <iostream>
using std::cout;
using std::endl;
class A {
public:
int data = 1;
};
class B : public A {
public:
int data = 2;
};
class C : private A {
};
void main(void) {
A * a = new B();
cout << a->data << endl;
B * b = new B();
cout << b->data << endl;
C * c = new C();
//cout << c->data << endl;//对于C来说,A的data是不可见的
//A *aa = new C();//不能使用A*,因为没有意义,在private下使用A*都是不可见的
}
27. 什么是纯虚函数,什么是抽象类?
答:虚函数后接=0就构成了纯虚函数。纯虚函数不需要实现,当然也可以去实现,虽然没有意义。存在纯虚函数的类称为抽象类,抽象类不能被实例化。
下面是一个例子:
#include <iostream>
using std::cout;
using std::endl;
class A {
public:
int data = 1;
A() {}
virtual void print() = 0;
};
void A::print() {
cout << "hello" << endl;
}
void main(void) {
A a;//报错
}