C++中将构造函数或析构函数定义为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也可以创建在栈上。

好的,这是一个经典的面向对象编程的例子,我来逐步解释一下。 首先,我们声明一个基类Shape,其中包含一个方法来计算面积: ```C++ class Shape { public: virtual double getArea() const = 0; // 纯虚函数,计算面积 virtual ~Shape() {} // 虚析构函数,确保子类析构时也会被调用 }; ``` 这里使用了纯虚函数来定义计算面积的功能,因为不同的形状计算面积的方法是不同的,我们需要在子类中实现这个方法。 接下来,我们从Shape派生两个类:Rectangle和Circle: ```C++ class Rectangle : public Shape { public: Rectangle(double w, double h) : width(w), height(h) {} double getArea() const override { return width * height; } private: double width, height; }; class Circle : public Shape { public: Circle(double r) : radius(r) {} double getArea() const override { return 3.14159 * radius * radius; } private: double radius; }; ``` 这里使用了override关键字来覆盖基类的虚函数,确保在子类中实现了正确的功能。Rectangle和Circle分别实现了自己的计算面积的方法,也定义了自己的构造函数。 接下来,我们从Rectangle派生正方形类Square: ```C++ class Square : public Rectangle { public: Square(double s) : Rectangle(s, s) {} }; ``` 注意到正方形是矩形的一种特殊情况,因此我们可以从Rectangle派生Square,然后在构造函数中将正方形的宽和高都设置为边长s。 最后,我们可以创建各派生类的对象,观察构造函数析构函数的调用次序,以及计算不同对象的面积: ```C++ int main() { Shape* s1 = new Rectangle(2, 3); Shape* s2 = new Circle(1.5); Shape* s3 = new Square(2); cout << "Area of rectangle: " << s1->getArea() << endl; cout << "Area of circle: " << s2->getArea() << endl; cout << "Area of square: " << s3->getArea() << endl; delete s1; delete s2; delete s3; return 0; } ``` 这里使用了指针来创建对象,并使用虚析构函数确保子类析构时也会被调用。我们可以观察到构造函数的调用顺序是Shape -> Rectangle -> Square/Circle,析构函数的调用顺序是Square/Circle -> Rectangle -> Shape,即与构造函数相反的顺序。 最后,我们计算了不同对象的面积,并输出了结果。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值