C++特殊类设计

本文介绍了C++中几种特殊类的设计技巧,包括禁止复制构造和赋值、限定在堆或栈上创建对象、禁止继承以及实现单例模式(饿汉和懒汉)。这些技术展示了如何控制类的行为以满足特定需求。
摘要由CSDN通过智能技术生成

不能被复制拷贝的类

思路:使得拷贝构造和赋值重载失效

  • C++11可以使用delete,删除对应的拷贝构造和赋值重载
  • C++98可以设置成私有的函数
//不能被复制的类,简单就是禁用拷贝构造和赋值重载
class NonCopy{
    public:
    NonCopy(){};
    
    
    // //直接删掉
    // NonCopy(const NonCopy& NC)=delete;
    // NonCopy operator=(const NonCopy& NC)=delete;
    //-------
    
    //下面是C++98做法,就是设置成私有
    private:
    NonCopy(const NonCopy& NC){};
    void operator=(const NonCopy& NC){};
};

只允许在堆上创建对象的类

思路:私有化构造函数,析构函数,拷贝构造函数。设计静态的funtion去提供堆上的对象以及Copy对应的对象

class OnlyCreatInHeap{
    //方法一:私有化其构造函数和析构函数     
    OnlyCreatInHeap()
    {
        cout<<"constructor is called\n";
    }
    ~OnlyCreatInHeap()
    {
        cout<<"destructor is called\n";
    }
    //就但凭借上述还不足够,还有个可以做到,就是拷贝构造,其可以借用现有对象去创建
    OnlyCreatInHeap(const OnlyCreatInHeap & it)
    {
        //拷贝构在此处
        cout<<"Copy constructor is called\n";
    }
    public:         
    //这个时候捏,对象大多数时候还没有创建,所以设置成static,防止乱使用this指针,也让外部调用更加便利
    static OnlyCreatInHeap* Creat()
    {
        return new OnlyCreatInHeap;
    } 
    static OnlyCreatInHeap* copy_construct(const OnlyCreatInHeap & it)
    {
        return new OnlyCreatInHeap(it);
    }
    void Des()
    {
        delete this;
    }  

};

void HeapOnly()
{
    // OnlyCreatInHeap a;//无法调用构造函数或者是析构是私有无法创建
    OnlyCreatInHeap*p = OnlyCreatInHeap::Creat();
    p->Des();
    // OnlyCreatInHeap a(*p);//此处会报错了
}

只允许在栈上创建的类

思路一:可以仿照堆得做法,禁用拷贝构造和构造函数,提供静态方法来给出栈的对象。

 1 class StackOnly
 2 {
 3 public:
 4     static StackOnly CreateObject()
 5     {
 6         return StackOnly();//确保了该类创建对象时不会使用new操作符,从而使得该类只能在栈上创建对象
 7     }
 8 private:
 9     StackOnly(){}//在堆和栈上创建对象都会调用构造函数,为了防止在堆上创建对象,应该将构造函数私有化
10 };

思路二:C++11可以用delete删除对应类里面的new构造,使得外部就不能调用new,这样的问题是该类的定位new也失效了。

class OnlyCreatInStack
{
    public:
    OnlyCreatInStack()
    {
        cout<<"constructor is called\n";
    }
    public:
    //这里返回临时对象就好了,像g++编译器,优化的十分厉害,检测到连续的构造拷贝构造,直接就修改作用域和生命周期以达到目的
    static OnlyCreatInStack CreatObj()
    {
        return OnlyCreatInStack();
    }
    //如同只在堆上创建一样,拷贝构造也应该想办法不要再new里面实现,但是又不能设为私有之类的操作,因为拷贝构造栈上也要用
    //有如下解决
    //实现专属的new运算符,这样这个类就new就会搞这个,然后给他delete
    void* operator new(size_t size) =delete;
    void operator delete(void* p)=delete;
};

// void*  OnlyCreatInStack:: operator new(size_t size)
// {
//     OnlyCreatInStack* p = (OnlyCreatInStack*)malloc(sizeof(OnlyCreatInStack));
//     ::new(p) OnlyCreatInStack;//调用用全局的定位new,也是不行的
//     return p;
// }
int Test_CreatOnlyInStack()
{
    OnlyCreatInStack a(OnlyCreatInStack::CreatObj());
    //OnlyCreatInStack* p = new OnlyCreatInStack;//这样new就行不通了
    // OnlyCreatInStack* p = new OnlyCreatInStack(a);//这样new就行不通了

    return 0;
}

设计不能继承的类

思路:C++11提供了关键字final,使得类不能被继承。或者也可以纯粹的私有化构造函数

class NonInherited  final
{

};

//这里直接就无通过语法检查
// class Son :public NonInherited
// {

// };

单例:设计一个类,只能有一个对象

设计一个类。使其只能有一个对象
首先根据特点,我们拒绝多次实例化和拷贝
显然是不允许存在拷贝构造,构造函数的,因此私有化
一般来说呢,私有化了拷贝构造,也应该私有化赋值重载

  • 饿汉模式:
//ok,上面的基本要求做完,接下来解决单例的构建问题
//饿汉模式:一开始就创建好,一般来说在main之前,那么我们思考在main函数之前,就创建的一般就是静态区
//:具备全局属性或者静态属性,因此可以在在类的局部里面操作这个问题
//优点:设计简单
//缺点:使得进程启动慢,甚至是卡住启动,如果单例有先后顺序,那么就无法控制
class OnlyOne{

    typedef OnlyOne Self;
    private:
    OnlyOne(const string& s = "",const int& a = 0)
    :m()
    {
        cout<<"OnlyOne begin"<<endl;
    }
    OnlyOne(const Self& him)
    {

    };
    Self operator=(const Self& him)
    {
        return *this;
    };
    ~OnlyOne()
    {
        cout<<"OnlyOne end"<<endl;
    }
    static OnlyOne _inst;
    map<string,int> m;
    public:
    static Self* GetPtr()
    {
        return &_inst;
    }
    void Add(const string& s,int k)
    {
        m[s] = k;
    }
    void Print()
    {
        for(auto it:m)
        {
            cout<<it.first<<' '<<it.second<<endl;
        }
    }
    
};

OnlyOne OnlyOne::_inst ;

int OnlyOne1()
{
    OnlyOne* p = OnlyOne::GetPtr();
    p->Add("1",2);
    p->Add("2",3);
    p->Print();
    return 0;
}
  • 懒汉模式:
//懒汉模式:使用的时候再实例化
//to do:线程安全问题没有解决   ################################
//new的懒汉都想一般不需要释放,因为程序结束就自动回收资源了
//僵尸进程,提前释放都很少遇见
//如果遇见要求写数据到磁盘,就需要调用析构函数
class OnlyOne2{
    private:
    OnlyOne2()
    {
        cout<<"OnlyOne2 begin"<<endl;
    }
    OnlyOne2(const OnlyOne2& s) = delete;
    OnlyOne2 operator=(const OnlyOne2& s) = delete;
    ~OnlyOne2()
    {
        
        cout<<"OnlyOne2 end"<<endl;
    }
    map<string,int> m;
    static OnlyOne2 *_inst;
    class gc{
        public:
        ~gc()
        {
            DelInst();
        }
    };
    static gc _gc;
    public:
    
    
    static OnlyOne2* GetPtr()
    {
        if(_inst){
            return _inst;
        }
        return _inst = new OnlyOne2;
    }
    void Add(const string& s,int k)
    {
        m[s] = k;
    }
    void Print()
    {
        for(auto it:m)
        {
            cout<<it.first<<' '<<it.second<<endl;
        }
    }
    static void DelInst()
    {
        if(_inst)
        {
            delete _inst;
            _inst = nullptr;
        }
    }
};

OnlyOne2* OnlyOne2:: _inst =nullptr;
OnlyOne2::gc OnlyOne2::_gc;
int main()
{ 
    OnlyOne2* p = OnlyOne2::GetPtr();
    p->Add("1",2);
    p->Add("2",3);
    p->Print();
    return 0;
}

上面代码具有线程安全问题,下面是解决的代码



#include <bits/stdc++.h>
using namespace std;
//我们来解决线程安全问题
//对于单例的懒汉模式而言,常用的是DCK,Double Check Locking模式
//这种模式在 访存是 ——分配内存 - 构建对象 - 赋值给指针 OK
//但是如果内存 重排序 -分配 - 赋值指针 - 构建对象;在多线程环境下就会出现问题
//会由于内存构造延迟性,导致拿到不完整构造的对象
//因此这里采用 C++11的atomic 来确保内存访问顺序
class OnlyOne2{
    private:
    OnlyOne2()
    {
        cout<<"OnlyOne2 begin"<<endl;
    }
    OnlyOne2(const OnlyOne2& s) = delete;
    OnlyOne2 operator=(const OnlyOne2& s) = delete;
    ~OnlyOne2()
    {
        
        cout<<"OnlyOne2 end"<<endl;
    }
    map<string,int> m;
    static atomic<OnlyOne2 *> _inst;
    static mutex _mut;
    class gc{
        public:
        ~gc()
        {
            DelInst();
        }
    };
    
    static gc _gc;
    public:
    
    
    static OnlyOne2* GetPtr()
    {
        if(_inst.load(memory_order_relaxed) ==nullptr)
        {
            lock_guard lk(_mut);
            auto tmp = _inst.load(memory_order_relaxed);
            //到这里和之前的代码实际上并无不同,用的都是最宽松的内存限制
            //到这里只有一个线程在执行
            if(tmp == nullptr)
            {
                tmp = new OnlyOne2;
                //禁止其他获取内存的操作排在此之前,获取访问内存,都使得内存构造完毕之后
                _inst.store(tmp,std::memory_order_consume);
            }
        }
        return _inst;

    }
    void Add(const string& s,int k)
    {
        m[s] = k;
    }
    void Print()
    {
        for(auto it:m)
        {
            cout<<it.first<<' '<<it.second<<endl;
        }
    }
    static void DelInst()
    {
        if(_inst)
        {
            delete _inst;
            _inst = nullptr;
        }
    }
};

atomic<OnlyOne2 *> OnlyOne2:: _inst =nullptr;
OnlyOne2::gc OnlyOne2::_gc;
mutex OnlyOne2::_mut;
int main()
{ 
    OnlyOne2* p = OnlyOne2::GetPtr();
    p->Add("1",2);
    p->Add("2",3);
    p->Print();
    return 0;
}
  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值