zoukankan      html  css  js  c++  java
  • 类声明、类作用域、前向声明、this指针、嵌套类、PIMPL 技法 等

    一、类声明

    //类是一种用户自定义类型,声明形式:
    class 类名称
    {
       public:
                 公有成员(外部接口)
       private:
                 私有成员
       protected:
                 保护成员
    };

    在关键字public后面声明,它们是类与外部的接口,任何外部函数都可以访问公有类型数据和函数。
    在关键字private后面声明,只允许本类中的函数访问,而类外部的任何函数都不能访问。
    在关键字protected后面声明,与private类似,其差别表现在继承与派生时对派生类的影响不同。

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    class Clock
    {
    public:
        void Display();
        void Init(int hour, int minute, int second);

    private:
        int hour_;
        int minute_;
        int second_;
    };

    假设定义了一个Clock 类,因为成员是private的,那么 Clock ck;  ck.hour_ = 12; 是错误的,对此定义一个public 的void SetHour(int hour) 来设置hour_ 的值。


    二、内联成员函数、成员函数的重载及其缺省参数

    这里有内联函数的概念。成员函数也可以是内联的,若在类内部实现,inline 关键字可加可不加;在类外部实现,需加inline,

    如 inline void Clock::SetHour(int hour) { } 。实际上即使加了inline也不一定宏展开,比如遇到switch,for 语句的时候就往往不会。

    此外,成员函数也像一般函数那样可以重载,也可以有缺省参数,参考这里


    三、类与结构体

    class与struct的区别:在未指定访问权限时,class默认的是私有的,struct默认是公有的,

    struct Test
    {
        int X;//公有的
         ...
    };

    此外,Test 可以独立作为一个tag,而不像C语言那样需要 struct Test 作为一个类型。


    四、隐含的 this 指针

    成员函数有一个隐含的附加形参,即指向该对象的指针,这个隐含的形参叫做this指针(编译器自动传递)

    使用this指针保证了每个对象可以拥有不同数值的数据成员,但处理这些成员的代码可以被所有对象共享

    成员函数是只读的代码,由所有对象共享,并不占对象的存储空间,因为this指针指向当前对象,所以成员函数可以区分它所作用的对象是哪一个。

    (哪个对象调用了this所在的函数,this就代表哪个对象)


    再来看一道经典的题目:

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    class A
    {
    public:
        int m;
        void print()
        {
            cout << "A" << endl;
        }
    };

    A *pa = 0;
    pa->print();

    可以理解为如下的C代码:

     C++ Code 
    1
    2
    3
    4
    5
    6
     
    void print(A *this)
    {
        cout << "A" << endl;
    }
    A *pa = 0;
    print_A(pa);

    相当于成员函数传递的this指针为0,那调用会出错吗? 肯定是正确输出"A" 的,因为this为0 表示没有对某个对象进行操作,而print里面确实没有对某

    个对象成员进行操作,所以是可以运行的。


    五、类作用域、前向声明

    (1)、每个类都定义了自己的作用域称为类作用域,类作用域中说明的标识符只在类中可见。除了类作用域,还有块作用域、文件作用域、函数原型作用域、函数作用域,举个例子:

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
     
    #include <iostream>
    using namespace std;

    class Test
    {
    public:
        int num_;
    };

    //num_ = 20;        Error,num_的作用域在类内部
    int num_ = 20;      // num_的作用域是文件作用域,与类中的num_是不同的作用域

    int add(int a, int b);  // a, b两个标识符的作用域为函数原型作用域

    int main(void)
    {
        int num_ = 30;      // num_为块作域
        {
            int num_ = 100; // num_为块作域
        }

        cout << num_ << endl;
        cout <<::num_ << endl;
        return 0;
    }

    int add(int a, int b)   // 形参a与b也算是块作用域
    {
        return a + b;
    }

    int test()
    {
    LABEL1: //函数作用域
        cout << "label1" << endl;
        goto LABEL3;
    LABEL2:
        cout << "label2" << endl;
        goto LABEL1;
    LABEL3:
        cout << "label3" << endl;
        goto LABEL2;
    }

    (2)、C++中类必须先定义,才能够实例化。两个类需要相互引用头文件形成一个“环形”引用时会出错。这时候需要用到前向声明,前向声明的类不能实例,但可以定义指针或引用。

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    #ifndef _B_H_
    #define _B_H_

    class A;

    class B
    {
    public:
        B(void);
        ~B(void);

        void fun(A &a)
        {

        }

        A *a_;      // 前向声明的类不能实例化对象
    };

    #endif // _B_H_
     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    #ifndef _A_H_
    #define _A_H_

    #include "B.h"
    class A
    {
    public:
        A(void);
        ~A(void);

        B b_;
    };

    #endif // _A_H_

    六、嵌套类、局部类

    (1)、嵌套类

    外围类需要使用嵌套类对象作为底层实现,并且该嵌套类只用于外围类的实现,且同时可以对用户隐藏该底层实现。

    从作用域的角度看,嵌套类被隐藏在外围类之中,该类名只能在外围类中使用。如果在外围类之外的作用域使用该类名时,需要加名字限定。

    嵌套类中的成员函数可以在它的类体外定义。

    嵌套类的成员函数对外围类的私有成员没有访问权,反之亦然。

    嵌套类仅仅只是语法上的嵌入

    (2)、局部类

    类也可以定义在函数体内,这样的类被称为局部类(local class)。局部类只在定义它的局部域内可见。

    局部类的成员函数必须被定义在类体中。

    局部类中不能有静态成员,关于类中的静态成员和静态成员函数以后再谈。

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
     
    #include <iostream>
    using namespace std;

    class Outer
    {
    public:
        class Inner
        {
        public:
            void Fun();
            //{
            //  cout<<"Inner::Fun ..."<<endl;
            //}
        };
    public:
        Inner obj_;
        void Fun()
        {
            cout << "Outer::Fun ..." << endl;
            obj_.Fun();
        }
    };

    void Outer::Inner::Fun()
    {
        cout << "Inner::Fun ..." << endl;
    }

    void Fun()
    {
        class LocalClass
        {
        public:
            int num_;
            void Init(int num)
            {
                num_ = num;
            }
            void Display()
            {
                cout << "num=" << num_ << endl;
            }

            //static int num2_; // 局部类内部不能定义静态成员
        };

        LocalClass lc;
        lc.Init(10);
        lc.Display();
    }

    int main(void)
    {
        Outer o;
        o.Fun();

        Outer::Inner i;
        i.Fun();

        Fun();
        //LocalClass lc;        Error,局部类只能在定义它的函数体中使用
        return 0;
    }


    七、PIMPL 技法

    来看下面的示例:

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    // file y.h
    #include "x.h"
    class Y
    {
        void Fun();
        X x_;

    };
    // file y.cpp
    #include "y.h"
    void Y::Fun
    {
        return x_.Fun();
    }
    // file main.cpp
    #include “y.h”
    int main(void)
    {
        Y y;
        y.Fun();
    }

    上面程序存在的问题是:

    1、引入更多的头文件,降低编译速度

    2、在编译期如果X的大小改变了,y.cpp 和 main.cpp 都得重新编译;在运行期,如果X有子类,也不能使用多态虚函数。

    3、假设y.cpp 编译成动态库给main.cpp 使用,当X的大小变化,动态库需要重新编译,此时main.cpp 因为有定义对象y ,故也需要

    重新编译。

    下面介绍一种PIMPL 技法,有人也把它当作一种设计模式:

    PIMPL(private implementation或pointer to implementation)也称为handle/body idiom

    PIMPL背后的思想是把客户与所有关于类的私有部分的知识隔离开。避免其它类知道其内部结构

    降低编译依赖、提高重编译速度

    接口和实现分离

    降低模块的耦合度

    编译期

    运行期

    提高了接口的稳定程度

    对于库的使用,方法不用改变

    对于库的编译,动态库的变更,客户程序不用重新编译

    修改后的程序:

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
     
    // file y.h
    class X;
    class Y
    {
        Y();
        ~Y();
        void Fun();
        X *px_;
    };
    // file y.cpp
    #include "x.h"
    Y::Y() : px_( new X ) {}
    Y::~Y()
    {
        delete px_;
        px_ = 0;
    }
    void Y::Fun()
    {
        return px_->Fun();
    }
    //  file main.cpp
    #include "y.h"
    int main(void)
    {
        Y y;
        y.Fun();
    }

    即Y 内部成员是X* 指针,在32位系统上,指针大小固定为4个字节,即使X大小改变,也不影响Y。如果X 有子类,通过基类指针px_ 

    还可以实现虚函数多态。


    参考:

    C++ primer 第四版
    Effective C++ 3rd
    C++编程规范

  • 相关阅读:
    跨域调用webapi web端跨域调用webapi
    如何通过js跨域调用ASP.NET Web API (请问如何实现在javascript中通过http get的方式跨域调用ASP.NET Web API?)
    MVC中使用SignaIR入门教程
    Axure RP是一个专业的快速原型设计工具
    angularJS 学习演示
    说说Angular中的$timeOut定时器
    镜像服务器文件实时监控同步程序
    服务器文件同步管理
    MySQL 数据库双向镜像、循环镜像(复制)
    vs2015密钥 企业版 专业版 (vs.net)
  • 原文地址:https://www.cnblogs.com/alantu2018/p/8471002.html
Copyright © 2011-2022 走看看