C++中将构造函数或析构函数定义为private

news/2024/5/18 23:25:46 标签: 构造函数, 析构函数, private

很多情况下要求当前的程序中只有一个object。例如一个程序只有一个和数据库的连接,只有一个鼠标的object。通常我们都将构造函数的声明置于public区段,假如我们将其放入private区段中会发生什么样的后果?这意味着什么?

        当我们在程序中声明一个对象时,编译器为调用构造函数(如果有的话),而这个调用将通常是外部的,也就是说它不属于class对象本身的调用,假如构造函数是私有的,由于在class外部不允许访问私有成员,所以这将导致编译出错。
        然而,对于class本身,可以利用它的static公有成员,因为它们独立于class对象之外,不必产生对象也可以使用它们。

        此时因为构造函数被class私有化,所以我们要创建出对象,就必须能够访问到class的私有域;这一点只有class的成员可以做得到;但在我们建构出其对象之前,怎么能利用它的成员呢?static公有成员,它是独立于class对象而存在的,“我们”可以访问得到。假如在某个static函数中创建了该class的对象,并以引用或者指针的形式将其返回(这里不以对象返回,主要是构造函数是私有的,外部不能创建临时对象),就获得了这个对象的使用权。
下面是例子:

class OnlyHeapClass
{
public:
    static OnlyHeapClass* GetInstance()
    {
        // 创建一个OnlyHeapClass对象并返回其指针
        return (new OnlyHeapClass);
    }
    void Destroy();
private:
    OnlyHeapClass() { }
    ~OnlyHeapClass() {}
};
 
int main()
{
    OnlyHeapClass *p = OnlyHeapClass::GetInstance();
    ... // 使用*p
    delete p;
    return 0;
}
 
        这个例子使用了私有构造函数,GetInstance()作为OnlyHeapClass的静态成员函数来在内存中创建对象:由于要跨函数传递并且不能使用值传递方式,所以我们选择在堆上创建对象,这样即使getInstance()退出,对象也不会随之释放,可以手动释放。
        构造函数私有化的类的设计保证了其他类不能从这个类派生或者创建类的实例,还有这样的用途:例如,实现这样一个class:它在内存中至多存在一个,或者指定数量个的对象(可以在class的私有域中添加一个static类型的计数器,它的初值置为0,然后在GetInstance()中作些限制:每次调用它时先检查计数器的值是否已经达到对象个数的上限值,如果是则产生错误,否则才new出新的对象,同时将计数器的值增1.最后,为了避免值复制时产生新的对象副本,除了将构造函数置为私有外,复制构造函数也要特别声明并置为私有。

        如果将构造函数设计成Protected,也可以实现同样的目的,但是可以被继承。

        另外如何保证只能在堆上new一个新的类对象呢?只需把析构函数定义为私有成员。

        原因是C++是一个静态绑定的语言。在编译过程中,所有的非虚函数调用都必须分析完成。即使是虚函数,也需检查可访问性。因些,当在栈上生成对象时,对象会自动析构,也就说析构函数必须可以访问。而堆上生成对象,由于析构时机由程序员控制,所以不一定需要析构函数。保证了不能在栈上生成对象后,需要证明能在堆上生成它。这里OnlyHeapClass与一般对象唯一的区别在于它的析构函数为私有。delete操作会调用析构函数。所以不能编译。

        那么如何释放它呢?答案也很简单,提供一个成员函数,完成delete操作。在成员函数中,析构函数是可以访问的。当然detele操作也是可以编译通过。
void OnlyHeapClass::Destroy() {
    delete this;
}
        构造函数私有化的类的设计可以保证只能用new命令在堆中来生成对象,只能动态的去创建对象,这样可以自由的控制对象的生命周期。但是,这样的类需要提供创建和撤销的公共接口。

        另外重载delete,new为私有可以达到要求对象创建于栈上的目的,用placement new也可以创建在栈上。

补充:
1.为什么要自己调用呢?对象结束生存期时不就自动调用析构函数了吗?什么情况下需要自己调用析构函数呢?   

        比如这样一种情况,你希望在析构之前必须做一些事情,但是用你类的人并不知道, 那么你就可以重新写一个函数,里面把要做的事情全部做完了再调用析构函数。 这样人家只能调用你这个函数析构对象,从而保证了析构前一定会做你要求的动作。

2.什么情况下才用得着只生成堆对象呢? 

        堆对象就是new出来的,相对于栈对象而言。什么情况下要new,什么情况下在栈里面 提前分配,无非就是何时该用动态,何时该用静态生成的问题。这个要根据具体情况具体分析。比如你在一个函数里面事先知道某个对象最多只可能10个,那么你就可以 定义这个对象的一个数组。10个元素,每个元素都是一个栈对象。如果你无法确定数 字,那么你就可以定义一个这个对象的指针,需要创建的时候就new出来,并且用list 或者vector管理起来。 


        类中“私有”权限的含义就是:私有成员只能在类域内被访问,不能在类域外进行访问。 

        把析构函数定义为私有的,就阻止了用户在类域外对析构函数的使用。这表现在如下两个方面: 

        1. 禁止用户对此类型的变量进行定义,即禁止在栈内存空间内创建此类型的对象。要创建对象,只能用 new 在堆上进行。 

        2. 禁止用户在程序中使用 delete 删除此类型对象。对象的删除只能在类内实现,也就是说只有类的实现者才有可能实现对对象的 delete,用户不能随便删除对象。如果用户想删除对象的话,只能按照类的实现者提供的方法进行。

        可见,这样做之后大大限制了用户对此类的使用。一般来说不要这样做;通常这样做是用来达到特殊的目的,比如在 singleton 的实现上。

        stackoverflow上面有这方面的说明用例(详情:http://stackoverflow.com/questions/1008019/c-singleton-design-pattern
--------------------- 
作者:yofer张耀琦 
来源:CSDN 
原文:https://blog.csdn.net/zyq522376829/article/details/48438037 
版权声明:本文为博主原创文章,转载请附上博文链接!


http://www.niftyadmin.cn/n/1281139.html

相关文章

08 Web全栈 软件架构(思维认知)

什么是架构师 架构师是技术开发领域中的特殊岗位,也是特别重要的岗位,架构师通常在软件设计到软件开发过程中,负责完整的流程设计和规范设计,制定软件的开发路线,迭代路线等内容。负责软件开发过程中的技术公关和核心…

速读原著-TCP/IP(窗口扩大选项)

第24章 TCP的未来和性能 24.4 窗口扩大选项 窗口扩大选项使T C P的窗口定义从16 bit增加为32 bit。这并不是通过修改T C P首部来实现的, T C P首部仍然使用 16 bit ,而是通过定义一个选项实现对 16 bit 的扩大操作 ( s c a l i n g o p e r a t i o n …

中国人现在最需要的不是科学技术,而是逻辑与哲学

为什么80%的码农都做不了架构师?>>> 中国人最需要的不是技术,是逻辑与哲学。建国以来的教育,把这两个最重要的东西给丢掉了,同时也丢掉了中国人的思考能力。 逻辑学不是思考的基础,它是一种对逻辑的形式化…

ipad上的电子阅读器们

经过此前一阵的各种电子书阅读器的对比,现在基本上主要使用QQ阅读来看书,但是QQ阅读对pdf的支持不是很好,基本是只负责给你显示出来根本不考虑你能不能看的水平。但是方正的Apabi却可以支持的很好,所以有的时候还需要使用apabi来看…

速读原著-TCP/IP(时间戳选项)

第24章 TCP的未来和性能 24.5 时间戳选项 时间戳选项使发送方在每个报文段中放置一个时间戳值。接收方在确认中返回这个数值,从而允许发送方为每一个收到的 A C K计算RT T(我们必须说“每一个收到的 A C K”而不是“每一个报文段”,是因为T…

优秀程序员要做到的十点

这篇文章要介绍的,是我作为专业程序员这些年来学到的能真正提高我的代码质量和整体工作效率的10件事情。 1. 永远不要复制代码 不惜任何代价避免重复的代码。如果一个常用的代码片段出现在了程序中的几个不同地方,重构它,把它放到一个自己的函…

muduo网络库源码分析——整体架构

muduo的源代码中,虽然不考虑可移植性,但还是划分了很多小的类(Channel、Socket、TcpConnection、Acceptor,不知道是不是参考了java中的概念),类之间大量通过boost::bind()注册回调函数,感觉比继…

速读原著-TCP/IP(PAWS:防止回绕的序号)

第24章 TCP的未来和性能 24.6 PAWS:防止回绕的序号 考虑一个使用窗口扩大选项的 T C P连接,其最大可能的窗口大小为 1千兆字节( 23 0)(最大的窗口是 6 5 5 3 521 4,而不是2 1 621 4,但只比这个…