C++面向对象

C++面向对象 自己对菜鸟教程的理解+总结,在Java的基础上进一步理解面向对象思想。 类和对象类:定义一个数据类型的蓝图,定义了类的对象包括什么,以及这个对

C++面向对象

自己对菜鸟教程的理解+总结,在Java的基础上进一步理解面向对象思想。

类和对象

  • 类:定义一个数据类型的蓝图,定义了类的对象包括什么,以及这个对象可以执行哪些操作

  • 对象通过直接访问运算符 . 来访问变量和方法

 1  class Box{
 2     public:
 3        double length;   // 盒子的长度
 4        double breadth;  // 盒子的宽度
 5        double height;   // 盒子的高度
 6        //成员函数可以在类的内部或外部定义
 7        double get(void){ //定义成员函数1
 8            return length * breadth * height;
 9        }
10        void set( double len, double bre, double hei );// 成员函数2声明
11  };
12  void Box::set( double len, double bre, double hei){ //定义成员函数2
13      //在类外部定义的成员函数要使用:类名 + 范围解析运算符:: + 函数名
14      length = len;
15      breadth = bre;
16      height = hei;
17  }
18 19  int main( ){
20     Box Box1;        // 声明 Box1,类型为 Box
21     Box Box2;        // 声明 Box2,类型为 Box
22     Box Box3;        // 声明 Box3,类型为 Box
23     // box 1 详述
24     Box1.height = 5.0; 
25     Box1.length = 6.0; 
26     Box1.breadth = 7.0;
27      // box 2 详述
28     Box2.height = 10.0;
29     Box2.length = 12.0;
30     Box2.breadth = 13.0;
31      // box 3 详述
32     Box3.set(16.0, 8.0, 12.0); 
33     volume = Box3.get(); 
34     cout << volume <<endl;
35     return 0;
36  }

 

类访问修饰符

  • public(公有成员):在类外部可以直接访问。

  • private(私有成员):私有变量或函数在类的外部不能直接访问,且不可查看,只有类和友元函数可以访问私有成员。

    默认情况下(不使用修饰符修饰时)类的所有成员都是私有的。

    实际操作中一般在私有区域定义数据,公有区域定义函数(公有区域的函数可以访问和修改私有变量

  • protected(受保护成员):protected和private类似,唯一区别是protected成员在子类中是可访问的。

 1  class Box{
 2     protected:
 3        double width;
 4  };
 5   
 6  class SmallBox:Box{ // SmallBox 是Box的子类
 7     public:
 8        void setSmallWidth( double wid );
 9        double getSmallWidth( void );
10        // 子类的成员函数,可以访问父类的protected成员
11        double getSmallWidth(void)
12        {
13            return width;
14        }
15  };

 

类的构造函数

  • 构造函数在每次创建类的新对象时执行,用于设置成员变量的初始值

 1  class Line{
 2     public:
 3        Line(void){   // 构造函数,构造函数名和类名相同
 4            cout << "Object is being created" << endl;
 5        }  
 6     private:
 7        double length;
 8  }; // 在每次创建一个新Line对象时都会输出Object is being created
 9 10  //默认的构造函数没有任何参数,如果带有参数,在创建对象时就会给对象赋初始值
11  class Line{
12     public:
13        Line(double len){ 
14            cout << "Object is being created" << endl;
15            length = len;
16        }
17        // 或者也可以使用初始化列表形式:
18        //Line(double len):length(len){}
19     private:
20        double length;
21  }; 

 

析构函数

  • 析构函数在每次删除对象时执行,一般用于关闭文件、释放资源等

1  class Line{
2     public:
3        //析构函数
4        ~Line(void){
5            cout << "Object is being deleted" << endl;
6        }
7     private:
8        double length;
9  };

 

友元函数

  • 友元函数定义在类外部,但有权访问类所有private成员和protected成员

  • 友元函数并不是类的成员函数,不属于任何类

 1  class Box{
 2      double width;
 3      public:
 4          //在类中使用friend声明友元函数
 5          friend void printWidth( Box box );
 6          void setWidth( double wid );
 7  };
 8  9  //友元函数在类外部定义,可以直接访问该类的任何成员
10  void printWidth( Box box ){
11      cout << "Width of box : " << box.width <<endl;
12  }

 

this指针

  • 每个对象都能通过this指针访问自己的地址,在成员函数内部调用可以指向调用成员函数的对象

  • 只有成员函数才有this指针,友元函数没有

static静态成员

  • 静态成员被这个类的所有对象共享

  • 静态成员函数没有this指针,只能访问静态成员

继承

访问控制

访问publicprotectedprivate
同一个类 yes yes yes
派生类 yes yes no
外部的类 yes no no
  • 子类不继承父类的情况:

    父类的构造函数、析构函数、拷贝构造函数

    父类重载的运算符

    父类的友元函数

多继承

  • C++类可以从多个类继承成员(Java只能通过接口实现多继承)

1  class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…
2  {
3      <派生类类体>
4  };

 

重载

  • 重载的定义:

    同一作用域中某个函数/运算符指定多个定义,与原函数具有相同名称的声明,但参数列表和具体实现不同

    当调用重载函数/重载运算符时,编译器会进行重载决策,即比较参数的类型,从而判断合适的实现

函数重载

 1  class printData
 2  {
 3     public:
 4        void print(int i) {
 5          cout << "整数为: " << i << endl;
 6        }
 7   
 8        void print(double  f) {
 9          cout << "浮点数为: " << f << endl;
10        }
11   
12        void print(char c[]) {
13          cout << "字符串为: " << c << endl;
14        }
15  };

 

运算符重载

 1  class Box
 2  {
 3     public:
 4        // 重载+运算符,用于把两个 Box 对象相加
 5        Box operator+(const Box& b)
 6        {
 7           Box box;
 8           //通过this访问运算符左边对象的属性(将左边对象作为参数进行传递)
 9           box.length = this->length + b.length;
10           box.breadth = this->breadth + b.breadth;
11           box.height = this->height + b.height;
12           return box;
13        }
14     private:
15        double length;      
16        double breadth;     
17        double height;      
18  };

 

 

多态

C++通过虚函数来实现多态

虚函数和纯虚函数

  • 虚函数

    父类的虚函数在调用不同的衍生类时可以通过子类的重写而拥有不同的功能。

    定义虚函数的目的是为了允许用父类的指针来调用子类的同名方法。

    “虚”表示"推迟联编"或者"动态联编",即具体调用的函数在运行时才被确定,编写代码时并不能确定。

    虚函数不代表函数不被实现,相反,虚函数必须在父类中实现(哪怕是空函数),纯虚函数才代表函数没有被实现。

    虚函数只能借助于指针或者引用来达到多态的效果。(即父类的指针/引用指向子类的对象,调用子类的函数,也就是通过父类来访问子类定义的函数)

  • 纯虚函数

    纯虚函数类似Java的接口,子类继承时必须重写

    含纯虚函数的类是抽象类,不能被实例化(因为有一个未知的函数)

    定义纯虚函数的目的:使子类只继承一个函数的接口

 1  class test{
 2  public:
 3        virtual void print();     //虚函数
 4     virtual void order()=0;      //纯虚函数
 5     int array[20];
 6  };
 7  void test::print(){
 8        order();
 9     printf("打印结果: ");
10     for(int i=0; i<20; i++)
11             printf("%d ", array[i]);
12  }

 

  • 析构函数/友元函数

    析构函数用于关闭资源,对不同类来说要被关闭的资源不同,所以析构函数应当是虚函数,但不一定是纯虚的

    友元函数不是成员函数,只有成员函数才可以是虚拟的,因此友元函数不能是虚函数,但可以通过友元函数来调用虚成员函数

覆盖和隐藏

  • 覆盖:

    指在父类和子类中,存在函数名、参数均相同的函数,且父类的该函数为虚函数,就会被子类的函数覆盖

  • 隐藏:

    1. 父类和子类中存在函数名相同,但参数不同的函数:无论父类函数是否为虚函数,父类函数都会被隐藏

    2. 父类和子类中存在函数名、参数均相同的函数:只有父类函数不为虚函数,父类函数才会被隐藏

总结:只有函数名参数都相同+虚函数才会被覆盖

关于覆盖和隐藏实际区别的解释:隐藏的函数在用父类的指针指向子类的对象后,仍然可以通过指针可以访问,覆盖则不行

数据抽象

抽象把代码分离为接口和实现。这样,如果改变底层实现,接口也将保持不变。

不管任何程序使用接口,接口都不会受到影响,只需要将最新的实现重新编译即可。

数据封装

通常情况下会将类成员设为private,保证良好的封装性。

需要访问时可以通过类向外提供的接口来访问private成员。

接口(抽象类)

抽象类为所有的外部应用程序提供一个适当的、通用的、标准化的接口。然后,子类通过继承抽象类,就把所有类似的操作都继承下来。

外部应用程序提供的功能(即公有函数)在抽象类中是以纯虚函数的形式存在的。这些纯虚函数在相应的子类中被实现。