天天看点

C++面试题记录(持续更新)

1、类模板

函数模板

template<typename T>
int compare(const T &v1, const T &v2)
{
	if(v1 < v2) return -1;
	if(v2 < v1) return 1;
	return 0;
}
           

类模板 

template <typename T>
class Complex{
    
public:
    //构造函数
    Complex(T a, T b)
    {
        this->a = a;
        this->b = b;
    }
    
    //运算符重载
    Complex<T> operator+(Complex &c)
    {
        Complex<T> tmp(this->a+c.a, this->b+c.b);
        return tmp;
    }
        
private:
    T a;
    T b;
}
           

2、虚函数、构造与析构

虚函数是C++中实现多态的一种方法,其中只声明不实现的虚函数称为纯虚函数,包含纯虚函数的类称为纯虚类,纯虚类不可以被实例化。

子类构造时先执行父类的构造函数,然后再执行子类的构造函数;析构时相反,先执行子类的析构函数,再执行父类的析构函数。

基类的析构函数要声明为虚函数,避免内存泄漏。

3、智能指针

share_ptr,unique_ptr, weak_ptr。

4、public,protect,private使用

public:类、子类和实例都可访问,继承时是public继承,子类可访问public成员,不可访问protect、private成员。

protect:类、子类可访问,实例不可访问。继承时基类中的所有成员在派生类中变成了protect。

private:类本身才可访问。继承时基类中的所有成员变成了private。

5、封装接口注意事项

6、对于一个空类,编译器默认做了哪些操作

默认添加了:构造函数、析构函数、拷贝构造函数、赋值运算符、取址运算符、this指针。

需说明的是,对于一个没有实例化的类,编译器是不会添加上述函数的,只有实例化时,才会默认生成相应的函数,同时,开辟一段内存空间。

update20190217:只有当上述函数被编译器需要时,才会被编译器默认添加。

具体场景参见:https://www.cnblogs.com/QG-whz/p/4676481.html

7、静态变量初始化时机、存储区域、是否线程安全

静态变量在编译过程中进去main()函数之前就已经被初始化了,C++在进入程序进入点前,编译器会执行StartUp代码,其作用为函数库的初始化、进行信息设立、I/OStream产生及static对象的初始化操作(调用构造函数)。静态变量存储在全局区,生命周期为整个程序的生命周期。静态对象在构造时是线程安全的,当有一个线程执行static类对象的构造函数时,其他欲访问该static类对象的线程都是阻塞的。但是在静态对象构造完毕后,其内部的静态成员是非线程安全的。

8、多重继承(菱形继承)

C++面试题记录(持续更新)

解决菱形继承的问题是使Base1和Base2这两个类继承Base的时候变为虚继承,即Base1:: virtual public Base, Base2::virtual public Base,这样实例出来的Derive对象中将只有一个base对象。

当然,也可以指定特定基类的方式访问基类变量,如下:

Derive d;

d.base =3; // 不正确

d.Base1::base = 3; // 正确

9、new和malloc的区别

malloc是标准库函数,new 是C++的运算符,都可用于申请动态内存。

由于malloc是库函数而不是运算符,不在编译器控制权限之内,不能把执行构造函数的任务加于malloc。

new运算符可以执行内存开辟、执行构造函数、内存的分配。

10、const 指针

理解const指针和指向const的指针技巧在于查看const关键字右边来确定什么被声明为常量。

const char *p 指向const char的指针,p可以改变指向,但是p指向的值不能改变;

char* const p 定义常量指针p,不可以改变其指向,但是可以通过指针来改变其指向的值。

以下程序的输出结果:

char str1[]       = "abc";  

char str2[]       = "abc";  

const char str3[] = "abc";   

const char str4[] = "abc";   

const char* str5  = "abc";  

const char* str6  = "abc";  

cout << boolalpha << ( str1==str2 ) << endl; // 输出什么?  

cout << boolalpha << ( str3==str4 ) << endl; // 输出什么?  

cout << boolalpha << ( str5==str6 ) << endl; // 输出什么?  

复制代码

【参考答案】

分别输出 false,false,true。

str1和str2都是字符数组,每个都有其自己的存储区,它们的值则是各存储区首地址,不等;

str3和str4同上,只是按const语义,它们所指向的数据区不能修改。

str5和str6并非数组而是字符指针,并不分配存储区,其后的“abc”以常量形式存于静态数据区,而它们自己仅是指向该区首地址的指针,相等。

11、模板函数是否声明和定义是否可以分开?

模板函数的声明和定义不能分开。编译的过程中,编译器遇到使用函数的地方时,指示链接器在其他的.obj文件中需找目标函数的实现体。模板函数只有在使用的地方才进行实例化,所以当编译器只看到模板的声明时,并不能实例化该模板,只能创建一个具有外部链接的符号并期待链接器能够将符号的地址决议出来。当

继续阅读