面试题一:设置一个不能被继承的类
思路:
子类继承父类的时候,子类的构造函数会自动调用父类的构造函数;子类的析构函数会自动调用父类的析构函数。所以只要将父类的构造函数和析构函数设置成私有的即可。(一旦设置成私有的,子类继承父类时,就会去调用父类函数和析构函数,从而导致编译器就会报错)
上述方法也存在一定的问题,将父类的构造函数和系统函数设置成私有的,如何实例化对象呢,又如何对不用的对象进行释放呢?
思路:
设置两个接口,分别用来创建对象和释放对象即可。
class A
{
public:
static A* GetObject()
{
return new A();
}
void destoryObject()
{
delete this;
}
private:
A() {};
~A() {};
};
void test()
{
A* a = A :: GetObject();
a-> GetDestory();
}
面试题二:设计一个类,只能在栈上开空间
法1:
思路:
只有使用new运算符,对象才会建立在堆上,所以只要我们禁用new运算符即可。
又知:执行new运算符有两步操作:①调用operator new() 开空间 ②调用构造函数初始化。
所以,只要将operator new() 声明成私有的即可。
class A
{
public:
A() {};
~A() {};
private:
void* operator new(size_t ret) {};
void operator delete(void* ptr) {};
};
法2:
思路:
上述方法已经提及new运算符的两步操作,所以我们还可以从另一操作下手
因为对象的构造是由构造函数完成的,那么将构造函数设置成保护的即可
但是有一个问题,这样一来,不是就无法在类外创建对象了吗?
思路:
只要在类中定义一个函数,用来返回这个对象即可。
注意:我们只能通过对象调用类的成员函数,将接口声明成静态的,我们就可以在类外调用了。
class B
{
public:
//提供接口
static B GetObject(int b)//静态函数
{
return B();
}
~B() {};
protected:
//将构造函数设置为保护
B(){};
private:
int _b;
};
面试题三:设计一个类,只能在堆上开空间
方法1:
思路:
只能在堆上开空间,意思说:不能调用类的构造函数来构造对象。
如果简单粗暴地将构造函数声明成私有的,不但无法在类外部调用调用类的构造函数构造对象;也会导致new运算符无法使用,因为调用构造函数是new运算符的操作步骤之一。
那么,我们可以如何设计这个类呢?
提示:
编译器在为类对象分配栈空间时,会先检查析构函数的访问性,其实不止是析构函数,还有其他的非静态函数。如果析构函数无法访问,那么编译器是不会为对象分配栈空间的。
所以,只要我们将析构函数设为私有,类对象就无法建立在栈上了。
因为析构函数声明成私有的,所以需要在类中再设一个接口,来释放内存。
class C
{
public:
C() {};
void destory()
{
delete this;
}
private:
~C() {};
};
方法1还是存在些许问题的,就是如果这个类被继承,析构函数通常被设为virtual
子类会对父类的析构函数进行重写,以实现多态。那如果将父类的析构函数设成私有的,就无法实现继承问题了。
思路:
将类的析构函数和构造函数都设为保护的即可。
再在类中定义两个接口用来创建对象和释放对象,以用来在类外调用。
class D
{
protected:
D() {};
~D() {};
public:
static D* create()
{
return new D();
}
void destory()
{
delete this;
}
};
void test()
{
D* d = D::create();
d->destory();
}
面试题四:单例模式
什么是单例模式?
单例模式:一个类只能创建一个对象
单例模式,可以保证类中只有一个实例,并提供一个访问它的全局访问点
单例模式有两种实现模式:
①饿汉模式
饿汉模式:不管你用不用,一上来就给你创建一个唯一的实例化对象出来。
优点:简单。
缺点:如果单例对象构造十分耗时或者占用很多资源;又或者这个对象根本就不会用到。
这种情况下,你一上来就初始化,会导致程序启动时缓慢
//饿汉模式
class Singleton
{
public:
Singleton* GetInstance()//此处就创建了唯一的对象
{
return &instance;
}
private:
Singleton() {};//构造函数私有化,外部便无法调用,无法实例化对象
//c++98写法:
Singleton(Singleton const& otherinstance);
Singleton& operator=(Singleton const& otherinstance);
//c++11写法:
Singleton(Singleton const& otherinstance) = delete;
Singleton& operator=(Singleton const& otherinstance) = delete;
static Singleton instance;
};
Singleton Singleton::instance;//在程序入口之前,就可以完成对单例对象的初始化
//因为这个类是在编译之前创建的,所以可以在main函数之前对其初始化
②懒汉模式
懒汉模式:延时加载,在需要时才创建对象。
缺点:复杂
优点:在需要时才会创建对象,不会白白申请无用对象,也不会造成空间浪费的情况。
//懒汉模式
class Singleton
{
public:
static Singleton* GetInstance()
{
//这里一定要使用双层加锁机制:double-check
//是因为加锁机制只能使得一个线程new对象,除非解锁
//但是我这个不为空,就不会new对象了
if (nullptr == pInstance)
{
mutex.lock();//加锁是为了防止多线程并行的情况,防止new出多个对象
if (nullptr == pInstance)
{
pInstance = new Singleton();
}
mutex.unlock();
}
return pInstance;
}
//实现一个内嵌的垃圾回收类
//一般内部类定义的目的,是为了帮外部类做一些事情
class GetGarbage
{
public:
~GetGarbage()
{
if (Singleton::pInstance)
{
delete Singleton::pInstance;
}
}
};
//定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象
static GetGarbage Garbage;
private:
Singleton() {};
Singleton(Singleton const&) = delete;
Singleton& operator=(Singleton const&)=delete;
static Singleton* pInstance;//单例对象指针
static mutex mutex;//互斥锁
};
Singleton* Singleton::pInstance = nullptr;
Singleton::GetGarbage Garbage;
mutex Singleton::mutex;