目录
在项目中总会有许多特殊的要求,而我们需要掌握一些特殊类的设计来满足需求。
一般我们设计的类,创建的对象可以在栈区,堆区,静态区,如何设计一些只能在一个存储区创建对象的类呢:
1. 只能在堆上创建的类
首先将构造函数私有化,防止直接在栈区和静态区创建对象,既然我们是在堆上创建,那么一定是动态开辟出来的对象,我们创建 CreateObj函数 用来返回动态开辟出的对象即可。
这样做还有一个问题,就是当我们调用创建对象的函数时,调用函数必须通过对象调用,而我们的对象还没有创建出来,相当于先有鸡还是先有蛋的问题。解决这个问题的突破口就是用 static 修饰创建对象的函数即可。
别忘记将拷贝构造函数设置成为删除函数,防止有人用 copy函数在栈上拷贝出对象
//仅在堆上创建对象
class HeapOnly
{
public:
static HeapOnly* CreateObj()
{
return new HeapOnly;
}
//防止老六用 copy 函数
HeapOnly(HeapOnly& h) = delete;
private:
//构造函数私有
HeapOnly()
{}
};
int main()
{
//HeapOnly h1; //栈
//static HeapOnly h2; //静态区
//HeapOnly* ph3 = new HeapOnly; //堆
HeapOnly* ph4 = HeapOnly::CreateObj();
//调用函数必须有对象,而要有对象那么必须调函数,解决方法:函数前static修饰
return 0;
}
2. 仅在栈上创建对象
仅在栈上创建对象,那么构造函数私有化,防止在静态区创建对象,创建 CreateObj 函数,返回在栈上创建的对象,同样的将拷贝构造设置成为删除函数
函数中对象还没有创建,返回对象时进行构造,一次拷贝构造,将临时变量赋值给 s1 时,也是一次拷贝构造,但是拷贝构造已经被设置成为了删除函数,按理说应该会报错,这里编译器会将连续的构造和拷贝构造优化成一次直接构造。
//仅在栈上创建对象
class StackOnly
{
public:
static StackOnly CreateObj()
{
return StackOnly(); //栈; 编译器直接优化成一次构造
}
//防止老六用 copy 函数
StackOnly(const StackOnly& h) = delete;
private:
//构造函数私有
StackOnly()
{}
};
int main()
{
StackOnly s1 = StackOnly::CreateObj(); //栈
//static StackOnly s2; //静态区
//StackOnly* ph3 = new StackOnly; //堆
return 0;
}
3. 禁止拷贝的类
C++98中:将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可
class CopyBan
{
private:
CopyBan(const CopyBan&);
CopyBan& operator=(const CopyBan&);
};
1. 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就不能禁止拷贝了
2. 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简
单,而且如果定义了就不会防止成员函数内部拷贝了。
C++11中:直接用delete将拷贝构造和赋值运算符重载设置成删除函数,就是不会调用这两个函数的意思,就可以防止对象的拷贝
//禁止拷贝的类
class CopyBan
{
public:
CopyBan(){}
CopyBan(const CopyBan&) = delete;
CopyBan& operator=(const CopyBan&) = delete;
private:
};
int main()
{
CopyBan c1;
CopyBan c2 = c1; //这里会报错
return 0;
}
4. 不能被继承的类
c++98中是直接将构造函数私有化,间接的体现出此类不能被继承
//不能被继承的类
//98是构造函数私有化
class NonInherit
{
public:
static NonInherit GetInstance()
{
return NonInherit();
}
private:
NonInherit()
{}
};
class A : public NonInherit
{
};
int main()
{
A a;
return 0;
}
c++11中直接给我们提供了关键字:final
直接在类名后修饰即可保证此类不能被继承。
//c++11
class NonInherit final
{
public:
static NonInherit GetInstance()
{
return NonInherit();
}
private:
NonInherit()
{}
};
class A : public NonInherit
{
private:
int _a;
};
int main()
{
A::GetInstance();
return 0;
}
只能创建一个对象的类--单例模式
一个类只能创建一个对象,即单例模式
该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
饿汉模式
程序启动时就创建一个唯一的实例对象
首先构造函数私有化,
在成员中创建一个静态的对象,对象是在静态区的,在类中声明,只是受到类域的限制而已,此对象并不是类中的对象;
为什么不将静态成员放在全局,因为构造函数私有,不能访问
静态成员在类中声明,类外定义
class InfoMgr //---饿汉举例
{
public:
static InfoMgr* GetInstance() //调用函数返回唯一对象
{
return _spinst;
}
void Setaddress(const string& s)
{
_address = s;
}
string& Getaddress()
{
return _address;
}
private:
InfoMgr() //构造函数私有化
{}
InfoMgr(const InfoMgr&) = delete; //防拷贝
string _address;
int _secretKey;
static InfoMgr* _spinst; //声明
};
InfoMgr* InfoMgr::_spinst = new InfoMgr; //定义
int main()
{
InfoMgr::GetInstance()->Setaddress("重庆");
cout << InfoMgr::GetInstance()->Getaddress() << endl;
return 0;
}
懒汉模式
第一次使用实例对象时,创建对象。
与饿汉不同的地方就在对象的定义及调用 GetInstance函数时才创建对象
class InfoMgr //---懒汉举例
{
public:
static InfoMgr* GetInstance()
{
if (_spinst == nullptr)
{
_spinst = new InfoMgr;
}
return _spinst;
}
void Setaddress(const string& s)
{
_address = s;
}
string& Getaddress()
{
return _address;
}
private:
InfoMgr()
{}
InfoMgr(const InfoMgr&) = delete; //防拷贝
string _address;
static InfoMgr* _spinst;
};
InfoMgr* InfoMgr::_spinst = nullptr;
int main()
{
InfoMgr::GetInstance()->Setaddress("重庆涪陵");
cout << InfoMgr::GetInstance()->Getaddress() << endl;
return 0;
}
饿汉懒汉的比较
饿汉特点:简单些,初始化顺序不确定,如果有依赖关系就会有问题。饿汉对象初始化慢且多个饿汉单例对象会影响程序启动。(对象多的时候启动程序加载过长)
懒汉特点:复杂一点。第一次调用时初始化,可以控制初始化顺序,延迟加载初始化,不影响程序启动(程序启动速度快)