一、设计一个类,不能被拷贝
禁掉拷贝构造和赋值重载。这样定义出来的对象不能用于拷贝构造,也可以防止自己给自己赋值。
class NoCopy
{
public:
NoCopy()
{
cout << "NoCopy()" << endl;
}
~NoCopy()
{
cout << "~NoCopy()" << endl;
}
private:
NoCopy& operator=(const NoCopy& nc);
NoCopy(const NoCopy& nc);
};
int main()
{
NoCopy nc;
// error NoCopy nc2(nc);
return 0;
}
二、类的对象只能在栈上创建
方法一:
将析构函数私有,由于定义普通对象的时候需要在调用构造和析构函数,就会因为调不到析构函数就报编译错误了,就保证了只能在栈上创建对象。
//1.封析构函数
class HeapOnly
{
public:
HeapOnly()
{
cout << "HeapOnly()" << endl;
}
void DestroyHeapOnly()
{
this->~HeapOnly();
}
private:
~HeapOnly()
{
cout << "~HeapOnly()" << endl;
}
};
int main()
{
//HeapOnly oy;
HeapOnly* poy = new HeapOnly;
poy->DestroyHeapOnly();
//delete poy;
return 0;
}
结果:
正确的调用了构造函数和析构函数。
方法二:
将构造函数私有化,与方法一类似,普通的对象或者静态对象都无法调用构造函数或者析构函数,相当于在调用的入口或者出口设置了障碍。
注意事项1:注意创建对象的函数GetInstance必须设置成static,不然就需要对象来调用,但是我们这里就是为了生成对象,就成了鸡生蛋,蛋生鸡的问题,所以static可以用类名调用函数,很好的解决了这个问题。
注意事项2:必须禁掉拷贝构造,因为拷贝构造可以利用已经有的对象重新构造一个对象,我们只封构造函数是不够的,而上面方法一没有封拷贝构造是因为拷贝对象最后也得调用析构函数,而析构函数被禁掉了。
注意事项3:
有了构造函数还会生成默认拷贝构造,有了构造函数就不会生成拷贝构造了。
//1.封构造函数
class HeapOnly
{
public:
//一定要设置static
static HeapOnly* GetInstance()
{
return new HeapOnly();
}
~HeapOnly()
{
cout << "~HeapOnly()" << endl;
}
//必须删除拷贝构造 C++11
//HeapOnly(const HeapOnly& on) = delete;
private:
//C++98
HeapOnly(const HeapOnly& on);
private:
HeapOnly()
{
cout << "HeapOnly()" << endl;
}
};
int main()
{
HeapOnly* pso = HeapOnly::GetInstance();
HeapOnly so(*pso);//调用了拷贝构造 err
delete pso;
return 0;
}
小总结:
C++98可以只声明不定义,最好是只声明,并且声明成私有,这样类外面即使实现,也无法调用。但C++11的话直接声明成删除函数即可(强烈推荐)。
即使将拷贝构造私有化,仍然可以在外面实现,在类内部的函数调用拷贝构造,所以最好使用C++11的delete。
class HeapOnly
{
public:
//一定要设置static
static HeapOnly* GetInstance()
{
return new HeapOnly();
}
~HeapOnly()
{
cout << "~HeapOnly()" << endl;
}
//必须删除拷贝构造 C++11
//HeapOnly(const HeapOnly& on) = delete;
private:
//C++98
HeapOnly(const HeapOnly& on);
public:
void func()
{
HeapOnly(*this);
}
private:
HeapOnly()
{
cout << "HeapOnly()" << endl;
}
};
HeapOnly::HeapOnly(const HeapOnly& on)
{
cout << "HeapOnly(const HeapOnly& on)" << endl;
}
int main()
{
HeapOnly* pso = HeapOnly::GetInstance();
pso->func();
//HeapOnly so(*pso);//调用了拷贝构造
delete pso;
return 0;
}
三、类的对象只能在栈上创建的对象
方法一:
定义类内部专属重载operator new为删除函数,这样能够有效的避免在堆上创建对象,但是防不了static对象,但是这种思想很好。
class StackOnly
{
private:
//C++98
//void* operator new(size_t size);
// void operator delete(void* ptr);
public :
//C++11
void* operator new(size_t size) = delete;
void operator delete(void* ptr) = delete;
StackOnly()
{
cout << "StackOnly()" << endl;
}
~StackOnly()
{
cout << "~StackOnly()" << endl;
}
};
int main()
{
StackOnly s;
//StackOnly* ps = new StackOnly; err
static StackOnly ss;
return 0;
}
方法二:
将构造函数和拷贝构造设置成私有,不过这两种方法都没办法避免静态对象的产生。因为编译器在这里会把构造和拷贝构造优化成一次构造,所以要使用对象的话可以用StackOnly::GetStackOnly()直接调用对应的成员函数。这样才可以不生成static对象
注意:不能删除拷贝构造,这里是将拷贝构造弄成私有,因为GetStackOnly当中需要调用拷贝构造将对象返回!!!!
class StackOnly
{
public:
static StackOnly GetStackOnly()
{
return StackOnly();
}
~StackOnly()
{
cout << "~StackOnly()" << endl;
}
public:
StackOnly(const StackOnly& so)
{
cout << "StackOnly(const StackOnly& so)" << endl;
}
StackOnly()
{
cout << "StackOnly()" << endl;
}
};
int main()
{
StackOnly s = StackOnly::GetStackOnly();
//StackOnly* ps = new StackOnly; err
static StackOnly ss = StackOnly::GetStackOnly();
return 0;
}
四、设置类不能被继承
方法一:
C++98下,构造函数私有,给一个创建实例的函数。此时可以继承,但是继承的子类无法创建实例,这种方法是一种间接实现的形式。
class NonInherit
{
public:
static NonInherit GetInstance()
{
return NonInherit();
}
~NonInherit()
{
cout << "~NonInherit()" << endl;
}
private:
NonInherit()
{
cout << "NonInherit()" << endl;
}
};
class D:public NonInherit
{
};
int main()
{
//D d;
return 0;
}
方法二(推荐):
final关键字,放在类名后面,继承就会编译错误。比前面的方法好。
class NonInherit final
{
public:
NonInherit()
{
cout << "NonInherit()" << endl;
}
~NonInherit()
{
cout << "~NonInherit()" << endl;
}
};
class D:public NonInherit
{
};
int main()
{
//D d;
return 0;
}
方法三(不推荐)::设置父类的成员函数为纯虚函数,
但是父类和子类都创建不出对象。并且子类可以重写纯虚函数来创建对象。
class NonInherit
{
public:
NonInherit()
{
cout << "NonInherit()" << endl;
}
virtual void func() = 0;
~NonInherit()
{
cout << "~NonInherit()" << endl;
}
};
class D:public NonInherit
{
};
int main()
{
D d;//err
return 0;
}
五、单例模式
概念:
一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。
在没有单例模式前,怎么才能让全局只有唯一一个实例呢?
假设我们有一个这样的情况,会报编译错误,为啥呢?comm.h的vector<int>commv
在other.cpp
和test.cpp
都会各有一份commv
,那么最后链接的过程就会有两个commv
。
报错说明: fatal error LNK1169: 找到一个或多个多重定义的符号,这个很正常。
那怎么解决?
方法一:
,static可以改变链接属性。好,那我们往v里面各自插入几个数据,出问题了,这个时候v里面只有后面插入的几个数据,为什么呢?
other.cpp
和test.cpp
各自有一个v,不保证全局是一个vector<int> v
,这可真是折磨人,万一在需要这样的场景出现这种问题,想必排查也要花费大量的时间。
方法二(推荐):
原因就在于在comm.h直接定义数组会让每一个包含他的.cpp各自拥有一个,那么我们在.h放声明,那么包含他的每一个cpp都只有声明,在链接的时候去comm.cpp去找定义就能保证大家拿到同一个vector<int> v;
引入单例模式:
也就是为了保证全局只有一个单例对象竟然如此麻烦,单例模式就解决了这个问题。单例模式又分为饿汉模式和懒汉模式。
饿汉模式在主线程起来的时候就把对象创建出来,若在构造函数做的工作比较复杂,进程会运行半天不起来。优点:代码实现简单;缺点:会导致启动前做的事情太多,拖慢启动速度。如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞争,提高响应速度更好。
懒汉模式则是主线程起来时创建一个单例对象的指针,等到第一个对象调用的时候再去初始化他的资源,这样对比饿汉模式只是加载一个指针,速度可想而知。
优点:启动速度快,可以解决类之间的依赖关系,而饿汉模式无法解决依赖关系
,因为谁先启动是不确定的。
缺点:多线程下导致初始化要加锁,实现比饿汉模式复杂。
饿汉模式:
注意要封掉拷贝构造。
并且.h类内部的static变量要在.cpp声明,直接在.h声明会直接报链接错误,编译器当作全局变量来看待,让他的static属性消失了。
赋值重载也可以封住,防止自己给自己赋值。
重点:
static放在类内部是声明,不是定义!!这里的static是与成员变量做区分,实际上是一个全局变量。并且他的链接属性不会变!表现在.hpp下定义会报链接错误,说明他不是static属性的,而应该是全局属性的
other.cpp
#include"Singleton.h"
void func2()
{
MySingleton& s = MySingleton::GetInstance();
//cout << &s << endl;
s.Push_Back(1111);
s.Push_Back(2222);
s.Push_Back(3333);
s.Push_Back(4444);
s.Push_Back(5555);
s.Print();
}
test.cpp
#include"Singleton.h"
void func1()
{
MySingleton& s = MySingleton::GetInstance();
//cout << &s << endl;
s.Push_Back(11);
s.Push_Back(22);
s.Push_Back(33);
s.Push_Back(44);
s.Push_Back(55);
s.Print();
}
int main()
{
func1();
func2();
}
Singleton.cpp
#include"Singleton.h"
MySingleton MySingleton::s;
Singleton.h
#pragma once
#include<iostream>
using namespace std;
#include<vector>
void func2();
class MySingleton
{
public:
//static MySingleton* GetInstance()
//返回对象引用和指针都可以,不影响。
static MySingleton& GetInstance()
{
return s;
}
MySingleton() = default;
MySingleton(const MySingleton& ms) = delete;
MySingleton& operator=(MySingleton& ml) = delete;
void Push_Back(int i)
{
v.push_back(i);
}
void Print()
{
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
private:
static MySingleton s;
vector<int> v;
};
// fatal error LNK1169: 找到一个或多个多重定义的符号
//MySingleton MySingleton::s;
注意:
下面的mutex是用了条件编译,当windows会调用windows的一套接口,linux的则是pthread的那一套接口。
懒汉模式:
other.cpp
#include"Singleton.hpp"
void func2()
{
MySingleton* s = MySingleton::GetInstance();
//cout << &s << endl;
s->Push_Back(1111);
s->Push_Back(2222);
s->Push_Back(3333);
s->Push_Back(4444);
s->Push_Back(5555);
s->Print();
}
Singleton.hpp
#pragma once
#include<iostream>
using namespace std;
#include<vector>
#include<mutex>
void func2();
class MySingleton
{
public:
static MySingleton* GetInstance()
{
if (s == nullptr)
{
//双检查提高效率
mlock.lock();
if (s == nullptr)
{
//设置s
s = new MySingleton();
//return s; error
}
mlock.unlock();
}
return s;
}
MySingleton() = default;
MySingleton(const MySingleton& ms) = delete;
void Push_Back(int i)
{
v.push_back(i);
}
void Print()
{
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
private:
static MySingleton* s;
vector<int> v;
static mutex mlock;
};
// fatal error LNK1169: 找到一个或多个多重定义的符号
//MySingleton MySingleton::s;
Singleton.cpp
#include"Singleton.hpp"
MySingleton* MySingleton::s = nullptr;
mutex MySingleton::mlock;
test.cpp
#include"Singleton.hpp"
void func1()
{
MySingleton* s = MySingleton::GetInstance();
//cout << &s << endl;
s->Push_Back(11);
s->Push_Back(22);
s->Push_Back(33);
s->Push_Back(44);
s->Push_Back(55);
s->Print();
}
int main()
{
func1();
func2();
}
注意这个问题,这里返回会让mlock带着锁离开,导致这里的锁出现问题。
报错:mutex destroyed while busy
总结
?_?