zoukankan      html  css  js  c++  java
  • c++类&对象

    1.类成员函数

    class Box
    {
       public:
          double length;         // 长度
          double breadth;        // 宽度
          double height;         // 高度
          double getVolume(void);// 返回体积
    };

    //类的成员函数
    double Box::getVolume(void) { return length * breadth * height; }

    成员函数可以定义在类定义内部,或者单独使用范围解析运算符 :: 来定义。
    函数如果直接在类中定义的话,则该函数默认为内联的(不太确定)。

    2.类访问修饰符

    public: // 公有成员
     
       公有成员在程序中类的外部是可访问的。您可以不使用任何成员函数来设置和获取公有变量的值
     
    protected: // 受保护成员
     
       保护成员变量或函数与私有成员十分相似,但有一点不同,保护成员在派生类(即子类)中是可访问的。
     
    private:// 私有成员
     
       私有成员变量或函数在类的外部是不可访问的,甚至是不可查看的。只有类和友元函数可以访问私有成员。
    
       默认情况下,类的所有成员都是私有的

    如果继承时不显示声明是 private,protected,public 继承,则默认是 private 继承,在 struct 中默认 public 继承

    继承方式

    3.构造函数&析构函数
    构造函数(有参和无参):

           类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。

           构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值

      使用初始化列表来初始化字段

    Line::Line( double len): length(len)
    {
        cout << "Object is being created, length = " << len << endl;
    }
    //上面的语法等同于如下语法:
    Line::Line( double len) { length = len; cout << "Object is being created, length = " << len << endl; }
    //假设有一个类 C,具有多个字段 X、Y、Z 等需要进行初始化,同理地,您可以使用上面的语法,只需要在不同的字段使用逗号进行分隔,
    C::C( double a, double b, double c): X(a), Y(b), Z(c)
    {
    ....
    }


    析构函数:

           类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。

           析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。

    4.c++拷贝构造函数

    拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于:

        • 通过使用另一个同类型的对象来初始化新创建的对象。

        • 复制对象把它作为参数传递给函数。

        • 复制对象,并从函数返回这个对象。

    如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。

    //拷贝构造函数的最常见形式
    classname (const classname &obj) {
       // 构造函数的主体
    }
    #include <iostream>
     
    using namespace std;
     
    class Line
    {
       public:
          int getLength( void );
          Line( int len );             // 简单的构造函数
          Line( const Line &obj);      // 拷贝构造函数
          ~Line();                     // 析构函数
     
       private:
          int *ptr;
    };
     
    // 成员函数定义,包括构造函数
    Line::Line(int len)
    {
        cout << "调用构造函数" << endl;
        // 为指针分配内存
        ptr = new int;
        *ptr = len;
    }
     
    Line::Line(const Line &obj)
    {
        cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;
        ptr = new int;
        *ptr = *obj.ptr; // 拷贝值
    }
     
    Line::~Line(void)
    {
        cout << "释放内存" << endl;
        delete ptr;
    }
    int Line::getLength( void )
    {
        return *ptr;
    }
     
    void display(Line obj)
    {
       cout << "line 大小 : " << obj.getLength() <<endl;
    }
     
    // 程序的主函数
    int main( )
    {
       Line line1(10);
     
       Line line2 = line1; // 调用了line2的拷贝构造函数,line2还没有构造

      //Line line3(20);
      //line3=line1; //这个地方才调用重载运算符!!!line3已经构造初始化过了
    display(line1); //对象作为参数传递给函数也会调用拷贝构造函数,不再调用构造函数 display(line2); return 0; }

    当上面的代码被编译和执行时,它会产生下列结果:

    调用构造函数                                   //line1的构造函数
    调用拷贝构造函数并为指针 ptr 分配内存             //line2的拷贝构造函数
    调用拷贝构造函数并为指针 ptr 分配内存             //display(line1)先调用拷贝构造,深拷贝一份line1
    line 大小 : 10                               //display(line1)内容打印大小
    释放内存                                      //display(line1)函数执行完,局部深拷贝拷贝出来的对象执行析构
    调用拷贝构造函数并为指针 ptr 分配内存             //同理display(line2)
    line 大小 : 10
    释放内存
    释放内存                       //最后两个释放为line2和line2执行的析构函数         
    释放内存

     总结:拷贝构造其实也是构造函数,在调用拷贝构造的三种情况下,无非就是通过深拷贝复制数据内容,但是新对象的地址是变的(记住拷贝构造也是构造函数)。可能有的小伙伴有误解,拷贝构造不是实现用的引用吗,新对象的地址应该没有变化啊。其实这个里面有一个误区,就是拷贝构造的引用其实是为了深拷贝原对象数据内容,这里的引用可以简单的认为就是使用原对象,而新对象其实已经开辟空间地址了(细探类开辟空间)。不信你看看构造函数,它也没有专门给开辟首地址。

    拷贝构造函数和赋值的区别

    • 用一个已存在的对象去构造一个不存在的对象(构造之前不存在),就是拷贝构造.
    • 用一个已存在的对象去覆盖另一个已存在的对象,就是赋值运算.
    • 拷贝构造函数从一个已经存在的变量来初始化一个新声明的变量,不需要清除现有的值(因为是新创建,所以没有现有值)
    • 拷贝构造函数没有返回值。
    • 赋值运算符return *this.(This is necessary to allow multiple assignment, eg x = y = z;)

    5.c++友元函数

      类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。

      友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。

    如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字 friend,如下所示:

    class Box
    {
       double width;
    public:
       double length;
       friend void printWidth( Box box );  //在类中声明了,但是并不是类的成员函数
       void setWidth( double wid );
    };

      声明类 ClassTwo 的所有成员函数作为类 ClassOne 的友元,需要在类 ClassOne 的定义中放置如下声明:

    friend class ClassTwo;

    使用友元函数注意的要点:

    1. 类中通过使用关键字friend 来修饰友元函数,但该函数并不是类的成员函数,其声明可以放在类的私有部分,也可放在共有部分。友元函数的定义在类体外实现,不需要加类限定。
    2. 一个类中的成员函数可以是另外一个类的友元函数,而且一个函数可以是多个类友元函数。
    3. 友元函数可以访问类中的私有成员和其他数据,但是访问不可直接使用数据成员,需要通过对对象进行引用
    4. 友元函数在调用上同一般函数一样,不必通过对对象进行引用。

    例子:

    #include <iostream>
    
    using namespace std;
    
    class Box
    {
        double width;
    public:
        friend void printWidth(Box box);
        friend class BigBox;
        void setWidth(double wid);
    };
    
    class BigBox
    {
    public :
        void Print(int width, Box &box)
        {
            // BigBox是Box的友元类,它可以直接访问Box类的任何成员
            box.setWidth(width);
            cout << "Width of box : " << box.width << endl;
        }
    };
    
    // 成员函数定义
    void Box::setWidth(double wid)
    {
        width = wid;
    }
    
    // 请注意:printWidth() 不是任何类的成员函数
    void printWidth(Box box)
    {
        /* 因为 printWidth() 是 Box 的友元,它可以直接访问该类的任何成员 */
        cout << "Width of box : " << box.width << endl;
    }
    
    // 程序的主函数
    int main()
    {
        Box box;
        BigBox big;
    
        // 使用成员函数设置宽度
        box.setWidth(10.0);
    
        // 使用友元函数输出宽度
        printWidth(box);
    
        // 使用友元类中的方法设置宽度
        big.Print(20, box);
    
        getchar();
        return 0;
    }

    6.c++内联函数(提升执行效率)
      函数调用是有时间和空间开销的。程序在执行一个函数之前需要做一些准备工作,要将实参、局部变量、返回地址以及若干寄存器都压入栈中,然后才能执行函数体中的代码;函数体中的代码执行完毕后还要清理现场,将之前压入栈中的数据都出栈,才能接着执行函数调用位置以后的代码。
      为了消除函数调用的时空开销,C++ 提供一种提高效率的方法,即在编译时将函数调用处用函数体替换,类似于C语言中的宏展开。这种在函数调用处直接嵌入函数体的函数称为内联函数(Inline Function),又称内嵌函数或者内置函数。指定内联函数的方法很简单,只需要在函数定义处增加 inline 关键字。

     #include <iostream>
     using namespace std;
     //内联函数,交换两个数的值
     //注意,要在函数定义处添加 inline 关键字,在函数声明处添加 inline 关键字虽然没有错,但这种做法是无效的,编译器会忽略函数声明处的 inline 关键字。
     inline void swap(int a, int b)
     {
         int temp;
         temp = a;
         a = b;
         b = temp;
        cout<<a<<", "<<b<<endl;
    }
     
     int main()
     {
         swap(1,2);
         swap(2,3);
         swap(3,4);
         return 0;
    }

      对于上面的程序,每次遇到“swap()”函数时,都会被替换为:“int temp;temp = a;a = b;b = temp;cout<<a<<", "<<b<<endl;”,就像C语言的宏替换一样,这样做的好处是什么呢?

      首先需要注意的是,上面写的“swap()”这个函数是在文件编译时就被替换了,也就是说最终的主函数里面实际上是很多条语句的,因为我调用了那么多次“swap()”函数,每个都被替换成上面绿色部分的那么多语句了,这样的好处就是,程序真正开始运行时,跑到这里时不用再去另外调用“swap()”函数,直接执行被替换的东西就行了,这样以来就省去了电脑在来回调用函数时的开销。

      上面说的内联函数的优点里面,其实同时也说出来了内联函数的缺点,那就是:内联函数你每调用一次,都会被最终替换成那么多语句,实际上就是编译器又为你每次都再把那些语句多写了一次,这样以来,程序的体积就变大了。

      所以,综上所述:一般只将那些短小的、频繁调用的函数声明为内联函数。如果一个函数频繁调用,但是这个函数很长,那就不适合声明为内联函数,要不然每替换一次,就又多出来那么多语句,程序体积就太大了。

    7.c++中的this指针

      在 C++ 中,每一个对象都能通过 this 指针来访问自己的地址。this 指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。

      友元函数没有 this 指针,因为友元不是类的成员。只有成员函数才有 this 指针。

    几点注意:

    • this 是 const 指针,它的值是不能被修改的,一切企图修改该指针的操作,如赋值、递增、递减等都是不允许的。
    • this 只能在成员函数内部使用,用在其他地方没有意义,也是非法的。
    • 只有当对象被创建后 this 才有意义,因此不能在 static 成员函数中使用(后续会讲到 static 成员)。

    this到底是什么?

      this 实际上是成员函数的一个形参,在调用成员函数时将对象的地址作为实参传递给 this。不过 this 这个形参是隐式的,它并不出现在代码中,而是在编译阶段由编译器默默地将它添加到参数列表中。
      this 作为隐式形参,本质上是成员函数的局部变量,所以只能用在成员函数的内部,并且只有在通过对象调用成员函数时才给 this 赋值。
      在《C++函数编译原理和成员函数的实现》一节中讲到,成员函数最终被编译成与对象无关的普通函数,除了成员变量,会丢失所有信息,所以编译时要在成员函数中添加一个额外的参数,把当前对象的首地址传入,以此来关联成员函数和成员变量。这个额外的参数,实际上就是 this,它是成员函数和成员变量关联的桥梁。

    8.c++中指向类的指针
      一个指向 C++ 类的指针与指向结构的指针类似,访问指向类的指针的成员,需要使用成员访问运算符 ->,就像访问指向结构的指针一样。与所有的指针一样,您必须在使用指针之前,对指针进行初始化。

    9.c++类的静态成员
      我们可以使用 static 关键字来把类成员定义为静态的。当我们声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本
      静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零。我们不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化

    静态成员函数

      如果把函数成员声明为静态的,就可以把函数与类的任何特定对象独立开来。静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符 :: 就可以访问。

      静态成员函数只能访问静态成员数据、其他静态成员函数和类外部的其他函数。

      静态成员函数有一个类范围,他们不能访问类的 this 指针。您可以使用静态成员函数来判断类的某些对象是否已被创建。

    静态成员函数与普通成员函数的区别:

    • 静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。
    • 普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针。
      #include <iostream>
       
      using namespace std;
       
      class Box
      {
         public:
            static int objectCount;
            // 构造函数定义
            Box(double l=2.0, double b=2.0, double h=2.0)
            {
               cout <<"Constructor called." << endl;
               length = l;
               breadth = b;
               height = h;
               // 每次创建对象时增加 1
               objectCount++;
            }
            double Volume()
            {
               return length * breadth * height;
            }
            static int getCount()
            {
               return objectCount;
            }
         private:
            double length;     // 长度
            double breadth;    // 宽度
            double height;     // 高度
      };
       
      // 初始化类 Box 的静态成员
      int Box::objectCount = 0;
       
      int main(void)
      {
        
         // 在创建对象之前输出对象的总数
         cout << "Inital Stage Count: " << Box::getCount() << endl;
       
         Box Box1(3.3, 1.2, 1.5);    // 声明 box1
         Box Box2(8.5, 6.0, 2.0);    // 声明 box2
       
         // 在创建对象之后输出对象的总数
         cout << "Final Stage Count: " << Box::getCount() << endl;
       
         return 0;
      }

      运行结果:

      Inital Stage Count: 0
      Constructor called.
      Constructor called.
      Final Stage Count: 2
  • 相关阅读:
    微信公众平台二次开发需要配置的几个地址与参数
    Extjs4.1+desktop+SSH2 定义程序入口
    Extjs4.1+desktop+SSH2 搭建环境 项目能跑起来
    Liunx开发(Extjs4.1+desktop+SSH2超强视频教程实践)(2)
    Liunx开发(Extjs4.1+desktop+SSH2超强视频教程实践)(1)
    增量补丁打包器(我也不是想这么干的)
    部署git服务器(Windows Server 2008)
    测试发布(maven-assembly-plugin看好你哦)
    工作流性能优化(敢问activiti有扩展性?)(3)
    工作流性能优化(敢问activiti有扩展性?)(2)
  • 原文地址:https://www.cnblogs.com/baconZhang/p/13823113.html
Copyright © 2011-2022 走看看