手机版
你好,游客 登录 注册
背景:
阅读新闻

教你如何用C++创建一个特殊的类

[日期:2017-04-17] 来源:Linux社区  作者:muhuizz [字体: ]

  就语言而言,个人还是比较喜欢C++,尽管 C++有些语法方面确实比较深奥,但这些确实挡不住它在实际应用中不可被替代的位置。

    开始谈今天的重点,如何定义一个特殊的C++类。

1、定义不可被继承的C++类
    如何让一个类不能被继承呢?简单来说,我们希望达到的效果,就是如果继承这个类的话,编译直接报错。
    实现这个类,我希望你提前了解过以下几个C++的简单语法:友元类、虚继承。这里我直接告诉你如何来定义,接下来我们讨论为什么。
    第一步:定义一个空类A,显式给出构造和析构,构造和析构必须定义为private。
    第二步:定义不可被继承的类B,给出正常的构造和析构,public类型,让 B虚继承A类,继承方式不限,同时将B设置为A的友元类。
    第三步:定义类C去尝试虚拟继承类B,编译错误!
代码如下:
class A
{
    friend class B;
private:
    A(){}
    ~A(){}
};
 
class B:virtual public A
{
public:
    B()
    {}
};
 
class C :public B
{
public:
    C()
    {}
};

我们来讨论,为什么编译会出错?
    首先,将类A的构造函数和析构函数定义为私有,如果没有显示定义,默认的构造和析构函数是public的。那定义为protected类型可以吗?答案是“NO”。了解过继承的话,应该知道,基类中private类型的成员变量或方法,在所有派生类中是不可见,但protected类型的成员变量或方法是可见的。我们的目的是如果以后构造类A的派生类时,需要调用类A构造,必须在类A内部调用该方法,即使是派生类,我也希望它遵守,即对派生类不可见。
    其次,类B继承了类A,继承方式无所谓,因为基类私有部分不论以何种方式继承,都是不可见的。为什么要虚继承呢?这个稍后说。同时,类B这里定义成类A的友元类,那么以后类B就可以直接访问类A的私有成员了,也就是说,这里的友元,打破了我们一开始定义的对派生类不可见的限制。那么以后在使用类B实例化对象时,完全可以成功,构造基类部分是通过友元实现的,和继承方式无关。这里的类B就是我们定义的不可被继承的类。
    最后,我们尝试让类C继承类B,那么当类C实例化对象时(或显式定义了构造函数),就会首先去构造属于类B的部分。这里注意,如果类B不是虚继承类A的话,那么这里构造属于类B的部分时,是通过类B构造类A部分,这必然是成功的。但我们不希望,因此,这里类B虚继承了类A,当构造属于类B部分时,由于B虚继承了A,那么会由类C直接去访问A,尝试构造类A的部分,很明显,由于访问不到类A的构造函数,因此C实例化对象失败。之后,所有尝试继承类B的类都会在编译时失败(前提要求C显示给出了构造,或类C实例化了对象)。

    到这里,不可被继承了类B创建成功,这里很巧妙的应用了友元类的概念,从而实现了仅有B可以实例化属于A的部分。

    如果觉得这种方式太过灵活,不容易理解,那么还有一种更加简单的方式。C++11引入了final关键字,被该关键字修饰的类,都是不可被继承的,这个和Java中的用法基本是一致的。

12345678 class A final
{
public:
    A(){}
};
 
class B :public A        // 编译出错
{};

2、定义一个只可以在栈上创建对象的类
    如果上面那种情况理解的话,这里应该不会太难。为什么只可以在栈上创建对象?如何实现?其实很简单,只要我们只对外暴露出可以在堆上创建对象的接口就可以。代码如下:

class A
{
public:
    static A* Get_A(int x)
    {
        return new A(x);
    }
    static void Delete_A(A* a)
    {
        delete a;
    }
private:
    A(int a = 10)
        :_a(a)
    {}
private:
    int _a;
};
 
int main()
{
    A* pa = A::Get_A(9);
    A* pb = A::Get_A(7);
    return 0;
}

    和之前一样,将构造函数和析构函数都设置为私有(因为这里不涉及继承,private和protected在这里没有区别),由于构造函数都是私有的,无法创建对象,因此,提供了静态方法,该方法中通过new和delete实现了在堆上对象的获取和释放。

3、定义一个只可以在栈上创建对象的类
    如果理解了上一种,就应该知道这里该如何去做。只要我们只提供在栈上获取对象的方式即可,由于栈空间是由操作系统维护了,没有特殊需要,析构函数就没有必要显式给出,代码如下:

class A
{
public:
    static A Get_A(int x)
    {
        return A(x);
    }
private:
    A(int a = 10)
        :_a(a)
    {}
private:
    int _a;
};
 
int main()
{
    A pa = A::Get_A(9);
    A pb = A::Get_A(7);
    return 0;
}

    只能在栈上或者只能在堆上创建的对象,实现起来原理是一样的,类的构造和析构函数都设置为私有,当我的接口函数提供的方法是从堆中创建的对象时,类就只能在堆上创建对象,当我的接口函数提供的是从栈上直接得到的对象的话,类就只可以在栈上创建对象。需要注意一点的是,创建栈上对象的时候,不可以返回临时对象的引用,这个就不再多解释。

本文永久更新链接地址http://www.linuxidc.com/Linux/2017-04/142843.htm

linux
相关资讯       C++类 
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数

       

评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款