剑指offer面试题--类
2016-07-04 22:42
302 查看
剑指offer和网上有很多考察类的面试题,我觉得很具有代表性,在这里做一个总结:
简答题一:我们可以用static修饰一个类的成员函数,也可以用const修饰类的成员函数?
答:错误的。一个类的成员函数使用const修饰,其意义是隐式的在参数中传一个const this*参数。然而,static修饰的成员函数是没有this指针的,与const是相反的。
简答题二:静态成员函数能不能同时也是虚函数?
答:不能。调用静态成员函数不需要实例,但调用虚函数需要从一个实例中指向虚函数表的指针以得到函数的地址,因此调用虚函数是需要一个实例的,所以这静态成员函数和虚函数是不能共存的。
一。剑指offer面试题2:实现一个单例模式的类。
要求:设计一个类,我们只能生成该类的一个实例。
该题目要求是这个类只能生成一个实例,所以我们要想办法控制类的构造函数,因为生成实例的入口都是构造函数。
解法一:将构造函数设置为私有的,对于类的外部将是不可见的。并且将它设置为静态的,只有当条件满足时唯一的调用一次
这种解法思路清晰且直观,但是这只适合单线程的模式下,如果多线程的情况下,会出现两个线程同时访问这个类,当遇到if语句时instance的值还为NULL,两个线程就都创建出一个实例,这就与题目要求不符合。
基于上面的解法一,我们可以得到可行解法二:
这样只够极低的几率下,通过越过了if (instance == NULL)的线程才会有进入锁定临界区的可能性,这种几率还是比较低的,不会阻塞太多的线程,但为了防止一个线程进入临界区创建实例,另外的线程也进去临界区创建实例,又加上了一道防御if (instance == NULL),这样就确保不会重复创建了。
二。剑指offer面试题48:不能被继承的类
要求:用C++设计一个不能被继承的类。
要想要实现一个不能继承的类在C#中是比较简单的,使用关键字sealed就行,所以在这里我们既要实现一个简易版的sealed关键字,是不是很牛的感觉,其实并不是很难得一件事。
解法一:要想要一个类不能继承,我们首先应该想到的是将类的构造函数设置为私有的,如果一个类的构造函数被设置为私有的,那么它的子类将会无法调用它的构造函数,这和不能继承是一样的效果。同题一一样,我们还想要得到这个类的一个实例并且释放它,这是作为一个类所应该具有的功能。我们可以通过定义公有的静态函数来创建和释放类的实例。
这种方法相信大家都可以想到,但是为了打动安静的面试官,我们得拿出点料来才行。
解法二:虚拟继承+友元类
继承于Base,Base为虚基类,因为它是Base的友元,所以,它可以访问基类的私有构造函数,以及析构函数。编译运行时是正确的。也就是说,可以创建堆上的对象,并且可以构建栈上的对象。
三。含有指针成员的类的拷贝
题目:下面是一个数组类的声明和实现,分析这个类有什么问题,并针对问标提出集中解决方案。
编译器生成的缺省的构造拷贝函数和拷贝运算符的重载函数,对指针实行的是按位拷贝,仅仅只是拷贝指针的地址,而不会拷贝指针的内容。若执行Array A(10);Array B(A)时。A.data和B.data指向的同一地址。当A或者B中任意一个结束其生命周期调用析构函数时,会删除data。由于他们的data指向的是同一个地方,两个实例的data都被删除了。但另外一个实例并不知道它的data已经被删除了,当企图再次用它的
data的时候,程序就会不可避免地崩溃。
为了防止这样的情况发生,我们同样将拷贝构造函数和赋值运算符的重载设为私有的函数。
简答题一:我们可以用static修饰一个类的成员函数,也可以用const修饰类的成员函数?
答:错误的。一个类的成员函数使用const修饰,其意义是隐式的在参数中传一个const this*参数。然而,static修饰的成员函数是没有this指针的,与const是相反的。
简答题二:静态成员函数能不能同时也是虚函数?
答:不能。调用静态成员函数不需要实例,但调用虚函数需要从一个实例中指向虚函数表的指针以得到函数的地址,因此调用虚函数是需要一个实例的,所以这静态成员函数和虚函数是不能共存的。
一。剑指offer面试题2:实现一个单例模式的类。
要求:设计一个类,我们只能生成该类的一个实例。
该题目要求是这个类只能生成一个实例,所以我们要想办法控制类的构造函数,因为生成实例的入口都是构造函数。
解法一:将构造函数设置为私有的,对于类的外部将是不可见的。并且将它设置为静态的,只有当条件满足时唯一的调用一次
<pre name="code" class="cpp"><span style="font-family:Microsoft YaHei;font-size:14px;">class Singleton { public: static Singleton GetInstance() { if(instance==NULL) { instance=new Singleton(); } return instance; } private: static Singleton(){}; static Singleton instance; }</span>
这种解法思路清晰且直观,但是这只适合单线程的模式下,如果多线程的情况下,会出现两个线程同时访问这个类,当遇到if语句时instance的值还为NULL,两个线程就都创建出一个实例,这就与题目要求不符合。
基于上面的解法一,我们可以得到可行解法二:
<pre name="code" class="cpp"><span style="font-family:Microsoft YaHei;font-size:14px;">class Singleton { public:</span>
<span style="font-family:Microsoft YaHei;font-size:14px;"> Singleton getInstance()</span>{ if (instance == NULL) {lock(); if (instance == NULL) { instance = new Singleton(); } unlock(); } return instance;}
<span style="font-family:Microsoft YaHei;font-size:14px;"> private: static Singleton(){}; static Singleton instance; }</span>
这样只够极低的几率下,通过越过了if (instance == NULL)的线程才会有进入锁定临界区的可能性,这种几率还是比较低的,不会阻塞太多的线程,但为了防止一个线程进入临界区创建实例,另外的线程也进去临界区创建实例,又加上了一道防御if (instance == NULL),这样就确保不会重复创建了。
二。剑指offer面试题48:不能被继承的类
要求:用C++设计一个不能被继承的类。
要想要实现一个不能继承的类在C#中是比较简单的,使用关键字sealed就行,所以在这里我们既要实现一个简易版的sealed关键字,是不是很牛的感觉,其实并不是很难得一件事。
解法一:要想要一个类不能继承,我们首先应该想到的是将类的构造函数设置为私有的,如果一个类的构造函数被设置为私有的,那么它的子类将会无法调用它的构造函数,这和不能继承是一样的效果。同题一一样,我们还想要得到这个类的一个实例并且释放它,这是作为一个类所应该具有的功能。我们可以通过定义公有的静态函数来创建和释放类的实例。
<span style="font-family:Microsoft YaHei;font-size:14px;">class A { public: static A * Construct(int n) { A *pa = new A; pa->num = n; cout<<"num is:"<<pa->num<<endl; return pa; } static void Destruct(A * pIntance) { delete pIntance; pIntance = NULL; } private: A(){} ~A(){} private: int num; }; </span>这种方法只可以创建堆上的对象,不可以构建栈上的对象。
这种方法相信大家都可以想到,但是为了打动安静的面试官,我们得拿出点料来才行。
解法二:虚拟继承+友元类
<span style="font-family:Microsoft YaHei;font-size:14px;">#include<iostream> using namespace std; template <typename T> class Base { friend T; private: Base() {} ~Base() {} }; class Finalclass : virtual public Base<Finalclass> { public: Finalclass() {} ~Finalclass() {} }; </span>Finalclass就是那个不能被继承的类。
继承于Base,Base为虚基类,因为它是Base的友元,所以,它可以访问基类的私有构造函数,以及析构函数。编译运行时是正确的。也就是说,可以创建堆上的对象,并且可以构建栈上的对象。
三。含有指针成员的类的拷贝
题目:下面是一个数组类的声明和实现,分析这个类有什么问题,并针对问标提出集中解决方案。
<span style="font-family:Microsoft YaHei;font-size:14px;"> Template<typename T> class Array { public: Array(unsigned arraySize):data(0), size(arraySize) { if(size > 0) data = new T[size]; } ~Array() { if(data) delete[] data; } void setValue(unsigned index, const T& value) { if(index < size) data[index] = value; } T getValue(unsigned index) const { if(index < size) return data[index]; else return T(); } private: T* data; unsigned size; }</span>眼尖的朋友应该看出来一点端倪,此类只定义了带参数的构造函数,没有定义无参数的构造函数,还有没有定义拷贝构造函数和复制运算符的重载,系统将会自动生成一个,但是,恰恰系统就会坑了你,此类中有指针的数据成员,这是很危险的
编译器生成的缺省的构造拷贝函数和拷贝运算符的重载函数,对指针实行的是按位拷贝,仅仅只是拷贝指针的地址,而不会拷贝指针的内容。若执行Array A(10);Array B(A)时。A.data和B.data指向的同一地址。当A或者B中任意一个结束其生命周期调用析构函数时,会删除data。由于他们的data指向的是同一个地方,两个实例的data都被删除了。但另外一个实例并不知道它的data已经被删除了,当企图再次用它的
data的时候,程序就会不可避免地崩溃。
为了防止这样的情况发生,我们同样将拷贝构造函数和赋值运算符的重载设为私有的函数。
<span style="font-family:Microsoft YaHei;font-size:14px;">private: Array(const Array& copy); const Array& operator = (const Array& copy);</span>但是这样的类并不是一个功能齐全的类,所以我们只能自己实现着两个函数来解决问题了:
<span style="font-family:Microsoft YaHei;font-size:14px;">rray(const Array& copy):data(0), size(copy.size) { if(size > 0) { data = new T[size]; for(int i = 0; i < size; ++ i) setValue(i, copy.getValue(i)); } } const Array& operator = (const Array& copy) { if(this == ©) return *this; if(data != NULL) { delete []data; data = NULL; } size = copy.size; if(size > 0) { data = new T[size]; for(int i = 0; i < size; ++ i) setValue(i, copy.getValue(i)); } }</span>
相关文章推荐
- 今天面试遇到的一些问题 【记录下来】
- Java实习面试的那些事(二)
- Android面试之J2SE基础
- Java实习面试的那些事(一)
- 剑指offer面试题30:最小的k个数
- 记搜狗机器学习实习生面试
- 面试题27:二叉搜索树和双向链表
- 树-面试题
- 各大IT公司经典面试题总结
- 剑指offer-3-面试14:调整数组顺序使奇数位于偶数前面
- .NET面试题系列[0] - 写在前面
- 面试总结
- 码农翻身
- 【IT我们来了】每一个程序员要遵守的一些优秀编程风格~
- 新手程序员如何高效编程
- 阿里巴巴2016前端开发实习生面试一面面经(总结)
- 面试时项目讲解
- 糟糕的程序员有哪些招牌特质?
- 作为一名程序员这些代码托管工具你都知道吗?
- 糟糕的程序员有哪些招牌特质?