设计一个只能在堆上创建对象的类
对于这种特殊类的设计我们一般都是优先考虑私有构造函数。然后对于一些特殊要求就直接通过静态成员函数的实现来完成。
class A//构造函数私有(也可以析构函数私有)
{
public:
static A* creat()
{
return new A;
}
private:
A()
{}
A(const A&) = delete;
A operator=(const A&) = delete;
int _a;
};
这里选择禁掉拷贝构造函数和拷贝函数是为了防止将已创建的对象去拷贝构造新的对象。
设计一个只能在栈上创建对象的类
class B
{
public:
static B creat()
{
B tmp;
return tmp;
}
//直接禁掉new和delete
//全局也有,类中也有,会优先调用类中的operator new(类中专属的)
void* operator new(size_t size) = delete;
void operator delete(void* p) = delete;
private:
B()
{}
int _b;
};
这里如果没有禁掉operator new和operator delete的话就会导致以下情况是在栈上创建对象
B* pb = new B(B::creat());//会调用拷贝构造
对于这种new一个对象的情况下是会调用拷贝构造函数的 ,而且我们是不能直接禁掉拷贝构造函数的,因为我们创建的对象必须通过调用拷贝构造函数来接受,所以就有了禁掉operator new和operator delete的方式。
对于在类中禁掉这两个函数,我们需要了解:当我们类中实现了operator new和operator delete这两个函数的话,在我们new该类对象或delete该类对象的话就会调用该类的operator new和operator delete函数,而不会选择调用全局的operator new和operator delete函数。
设计一个不能被继承的类
class D final
{
D()//私有构造也可以
{}
};
这里需要认识一个关键词final,该关键词修饰得类会不允许被继承。
而且还有一点,我们知道继承一个类之后,子类创建对象会调用父类的构造函数,如果父类没有默认的构造函数的话,子类必须要显示的调用父类的构造函数。
设计一个只能创建一个对象的类(单例模式)
单例模式:一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。
单例模式有以下两种实现方式:
饿汉模式
饿汉模式就是不管你用不用该实例,系统在启动程序时就会直接创建一个唯一的实例。
class A
{
A(const A& tmp) = delete;//禁掉拷贝构造,防止再次创建对象
A& operator=(const A& tmp) = delete;
public:
static A& getinstance()//通过成员函数得到该对象
{
return sigle;
}
void Add(string s1, string s2)
{
_dict[s1] = s2;
}
void Print()
{
for (auto tmp : _dict)
{
cout << tmp.first << ":" << tmp.second << endl;
}
}
private:
A()//只能创建一个对象,构造私有
{
cout << "构造完成" << endl;
}
map<string, string> _dict;
static A sigle;//声明 类里面静态成员可以直接调用私有函数
};
A A::sigle;//定义(此时已经调用好了构造函数)
对于单例模式一般就是在类中提前声明好该静态对象 所以在定义自定义类型的的时候就会直接调用构造函数。而这sigle对象就是我们所创建的唯一实例。
懒汉模式
懒汉模式就是在你开始调用的时候才会创建对象。
class B
{
B(const B& tmp) = delete;//禁掉拷贝构造,防止再次创建对象
B& operator=(const B& tmp) = delete;
public:
static B* getinstance()
{
if (sigle == nullptr)//只有为空才创建
sigle = new B;//一般不需要释放,进程结束的时候会释放
return sigle;
}
static void del()
{
delete sigle;//为空就不会再调用析构函数
sigle = nullptr;
}
void Add(string s1, string s2)
{
_dict[s1] = s2;
}
void Print()
{
for (auto tmp : _dict)
{
cout << tmp.first << ":" << tmp.second << endl;
}
}
private:
B()//只能创建一个对象,构造私有
{
cout << "构造完成" << endl;
}
~B()
{
//持久化:要求数据写到文件中
cout << "数据录入文件中 并析构" << endl;
}
map<string, string> _dict;
static B* sigle;
//内部类是外部类的友元
class gc//类似智能指针,程序结束前调用析构
{
public:
gc()
{
cout << "gc()" << endl;
}
~gc()
{
del();
}
};
static gc _gc;//声明 创建静态成员,属于一个类,不会创建多份
//程序结束前就会自动调用gc析构函数
};
B* B::sigle = nullptr;//初始化为空
B::gc B::_gc;//定义 main结束会调用构造
对于懒汉模式和饿汉模式本质区别就是对象和指针的转变,饿汉模式的唯一实例是一个静态的类指针,但是该指针的释放就会有点困难,需要我们手动去delete,其实进程结束也是会释放内存的,但是对于一些需要持久化的将数据写到文件的情况时就会采用内部类gc来解决(内部类是外部类的友元,可以访问外部类的所有成员)而同样在外部类中创建静态成员,类外进行定义(调用构造),所以当main函数结束前,对于自定义的该成员就会自动调用析构函数,此时就可以进行持久化处理。
对于以上代码如果_gc不设为外部类的静态成员而是一般成员的话,就会陷入循环析构,因为对于外部类来说_gc属于类的自定义成员,所以当调用外部类的析构时就会先调用自生的析构函数再调用自定义成员_gc的析构函数,此时就会造成循环析构的情况。
int main()
{
B::getinstance()->Add("sort", "排序");
B::getinstance()->Add("left", "左边");
B::getinstance()->Print();
//B::del();
return 0;
}