C++编程面向对象入门全面详解

编辑: admin 分类: c#语言 发布时间: 2021-12-12 来源:互联网
目录
    • 1. struct和class的区别
    • 2. explicit构造
    • 3. const和mutable
    • 4. 自引用
    • 5. static
      • 复数的实现
    • 6.成员函数重载
      • 7.运算符重载
        • 8.new
          • 9.析构函数
            • 10.friend
              • 11.类的继承
                • 12.多态
                  • 13.virtual

                  1. struct和class的区别

                  如果从C语言的视角来看,所谓类就是能够调用自身成员的结构体。而在C++中,关键字struct虽然仍旧保留,但已非C语言中的结构体,而是表示默认成员共有的class

                  即在C++中,struct C{/*code*/}class C{public:/**/}并无区别,例如下面两组代码所实现的功能是完全一致的。

                  //默认成员公有
                  struct Number{
                  private;
                      float val;
                  public:
                      float pubVal;
                      Number(float inVal);
                  };
                  
                  //默认成员为私有
                  class Number{
                      float val;//外部无法直接访问
                  public:
                      float pubVal;
                      Number(float inVal);
                  };
                  

                  所谓私有成员,就是外部函数不可访问的成员

                  void printPublic(Number num){
                      cout<<num.pubVal<<endl;
                  }
                  void printPrivate(Number num){
                      cout<<num.val<<endl;        //报错,无法访问私有类型
                  }
                  

                  不过从C语言的视角来看,类也的确保留了一些struct的风格,其初始化方法与指针调用便是明证。

                  int main(){
                      Number num{3.14};       //相当于引用构造函数
                      printNumber(num);
                      Number* pNum = &num;    //指向num的指针
                      //->表示类指针所指向的成员
                      cout<<pNum->pubVal<<endl;
                      system("pause");
                      return 0;
                  }
                  

                  输出为

                  PS E:\Code\cpp> g++ .\oop.cpp
                  PS E:\Code\cpp> .\a.exe      
                  3.14
                  3.14
                  

                  2. explicit构造

                  由于C++对泛型具备十分良好的支持,语言本身的强大可能会导致用户在使用过程中不严谨,继而增大维护成本。例如对于如下构造函数

                  Number::Number(float inVal){
                      val = inVal;
                  }
                  

                  那么下面的几个语句都能够输出正确的值

                  int main(){
                      Number num{3.14};
                      printNumber(num);
                      num = 1.414;
                      printNumber(num);
                      printNumber(0.618);
                      system("pause");
                      return 0;
                  }
                  

                  结果为

                  PS E:\Code\cpp> g++ .\oop.cpp
                  PS E:\Code\cpp> .\a.exe
                  3.14
                  1.414
                  0.618
                  请按任意键继续. . .
                  

                  可见这三条语句都没有报错

                      Number num{3.14};
                      num = 1.414;
                      printNumber(0.618);
                  

                  第一条是没有问题的,是简单赋值语句;第二条和第三条则是暗中调用构造函数,将浮点类型的变量转换成了Number类型,这种意义不明的代码自然会引起维护上的困难。explicit就为解决这种问题而生的。

                  将构造函数用explicit进行标记,可以有效禁止这种隐式转换

                  class Number{
                      float val;
                  public:
                      explicit Number(float inVal);
                      float pubVal;
                  };
                  int main(){
                      Number num{3.14};
                      num = 1.414;        //编译不予通过
                      printNumber(0.618);//编译不予通过
                      //...
                  }
                  

                  3. const和mutable

                  顾名思义,二者分别是常量与变量,前者要求成员函数不得修改类的成员变量

                  class Number{
                      float val;
                  public:
                      mutable float pubVal;       //注意该变量用了mutable
                      explicit Number(float inVal);
                      void printVal() const;      //该方法用了const
                  };
                  void Number::printVal() const{
                      cout<<val<<endl;
                      /*
                      val = val+1;   //这是不被允许的
                      */
                     pubVal = val+1;  //这是被允许的
                  }
                  

                  即,const成员只能修改mutable成员。

                  4. 自引用

                  自引用是一种编程技巧,对于更改类状态的函数,如果将类本身作为返回值,那么就可以实现炫酷而优雅的链式操作。

                  class Number{
                      float val;
                  public:
                      explicit Number(float inVal);
                      Number& addOne();       //其返回值是当前对象的地址
                  };
                  Number& Number::addOne(){
                      cout<<val++<<endl;
                      return *this;           
                  }
                  

                  其中,*this指向调用该成员函数的对象,测试一下

                  int main(){
                      Number num{3.14};       //相当于引用构造函数
                      num.addOne().addOne().addOne();
                      system("pause");
                      return 0;
                  }
                  

                  结果为

                  PS E:\Code\cpp> g++ .\oop.cpp
                  PS E:\Code\cpp> .\a.exe      
                  3.14
                  4.14
                  5.14
                  请按任意键继续. . . 
                  

                  5. static

                  顾名思义,静态成员之所以被称为静态,在于其存储位置只有一个。对于一个类而言,无论创建了多少实例,类中的静态变量就只被存储在那一个位置。这意味着静态成员要比对象实例具有更长的生命周期,当一个对象被销毁之后,静态成员并没有被销毁,从而再次被调用的时候,也不必另行分配内存。

                  class Number{
                      float val;
                      static Number defaultNum;
                  public:
                      explicit Number(float inVal=0);
                      static void setDefault(float inVal);
                      void printVal() const;
                  };
                  void Number::printVal() const{
                      cout<<val<<endl;
                  }
                  //定义默认Num
                  Number Number::defaultNum{3.14};
                  void Number::setDefault(float val){
                      defaultNum = Number{val};
                  };
                  Number::Number(float inVal){
                      val = inVal ? inVal : defaultNum.val;
                  }
                  int main(){
                      Number num{};       //相当于引用构造函数
                      num.printVal();
                      system("pause");
                      return 0;
                  }
                  

                  输出为

                  PS E:\Code\cpp> .\a.exe      
                  3.14
                  请按任意键继续. . . 
                  

                  复数的实现

                  复数有实部和虚部,默认值为0,其加法和减法分别就是实部和虚部相减,其乘法为

                  在这里插入图片描述

                  #include<iostream>
                  using namespace std;
                  class Complex{
                      float real;     //实部
                      float im;       //虚部
                      static Complex defaultNum;
                  public:
                      explicit Complex(float inReal=0, float inIm=0);
                      static void setDefault(float inReal, float inIm);
                      void printVal() const;
                      Complex& add(float inReal, float inIm);
                      Complex& minus(float inReal, float inIm);
                      Complex& multi(float inReal, float inIm);
                      Complex& div(float inReal, float inIm);
                  };
                  //默认值为{0,0}
                  Complex Complex::defaultNum{0,0};
                  void Complex::setDefault(float inReal,float inIm){
                      defaultNum = Complex{inReal, inIm};
                  };
                  //打印当前值
                  void Complex::printVal() const{
                      cout<<"real part: "<<real<<endl;
                      cout<<"image part:"<<im<<endl;
                  }
                  //加法
                  Complex::Complex(float inReal, float inIm){
                      real = inReal ? inReal : defaultNum.real;
                      im = inIm ? inIm : defaultNum.im;
                  }
                  Complex& Complex::add(float inReal, float inIm){
                      real += inReal ? inReal : 0;
                      im += inIm ? inIm : 0;
                      return *this;
                  }
                  Complex& Complex::minus(float inReal, float inIm){
                      real -= inReal ? inReal : 0;
                      im -= inIm ? inIm : 0;
                      return *this;
                  }
                  Complex& Complex::multi(float inReal, float inIm){
                      float temp = real*inReal - im*inIm;
                      im = real*inIm + im*inReal;
                      real = temp;
                      return *this;
                  }
                  Complex& Complex::div(float inReal, float inIm){
                      float temp = inReal*inReal + inIm*inIm;
                      float tempReal = (real*inReal + im*inIm)/temp;
                      im = (im*inReal-real*inIm)/temp;
                      real = tempReal;
                      return *this;
                  }
                  int main(){
                      Complex num{};       //相当于引用构造函数
                      num.add(1,2).multi(3,4).div(1,2);
                      num.printVal();
                      system("pause");
                      return 0;
                  }
                  

                  下面的操作便基于这个复数类进行。

                  6.成员函数重载

                  上述的加减乘除运算,默认输入值为实部和虚部的组合,但并不能实现两个Complex的运算。C++支持成员函数的重载。

                  class Complex{
                  /*
                  上文中所定义的类的结尾
                  */
                      Complex operator+(Complex);
                      Complex operator-(Complex);
                      Complex operator*(Complex);
                      Complex operator/(Complex);
                      //实现类似数乘功能
                      Complex operator*(float);
                      Complex operator/(float);
                  }
                  

                  这些函数可以通过最简单的方式定义

                  Complex& Complex::add(Complex num){
                      real += num.real;
                      im += num.im;
                      return *this;
                  }
                  

                  也可以通过调用已经定义过的成员函数

                  Complex& Complex::multi(Complex num){
                      multi(num.real, num.im);
                      return *this;
                  }
                  

                  7.运算符重载

                  在C++中,可以很方便地对一些运算符进行重载,其格式为

                  Complex operator+(Complex);
                  

                  对于两个复数a和b来说,调用重载之后的运算符a+b等价于a.operator(b)

                  其具体实现为

                  class Complex{
                  /*
                  上文中所定义的类的结尾
                  */
                      Complex operator+(Complex);
                      Complex operator-(Complex);
                      Complex operator*(Complex);
                      Complex operator/(Complex);
                  }
                  Complex Complex::operator+(Complex num){
                      float outReal = real+num.real;
                      float outIm = im+num.im;
                      return Complex{outReal, outIm};
                  }
                  Complex Complex::operator-(Complex num){
                      return Complex{real-num.real, im-num.im};
                  }
                  Complex Complex::operator*(Complex num){
                      return Complex{real*num.real - im*num.im,
                                     real*num.im + im*num.real};
                  }
                  Complex Complex::operator/(Complex num){
                      float temp = num.real*num.real + num.im*num.im;
                      return Complex{(real*num.real + im*num.im)/temp,
                                     (im*num.real-real*num.im)/temp};
                  }
                  Complex Complex::operator*(float val){
                      return Complex{real*val,im*val};
                  }
                  Complex Complex::operator/(float val){
                      return Complex{real/val,im/val};
                  }
                  //主函数
                  int main(){
                      Complex temp{1,1};
                      Complex temp1 = temp-temp*temp*2;
                      temp1.printVal();
                      temp.printVal();
                      system("pause");
                      return 0;
                  }
                  

                  测试一下结果为

                  PS E:\Code\cpp> g++ .\oop.cpp
                  PS E:\Code\cpp> .\a.exe      
                  real part: 1
                  image part:-3
                  real part: 1
                  image part:1
                  

                  可见操作符虽然被重载了,但运算次序得以保留。

                  8.new

                  C语言中通过STRUCT* struct = (STRUCT*)malloc(sizeof(STRUCT))的方式来动态地开辟内存,留待日后使用。

                  在C++中,new可以胜任这一工作。

                  例如

                  int* p = new int;
                  int* Q = new int(5);
                  

                  对于Complex类,可以通过指针形式进行实现

                  int main(){
                      Complex* temp = new Complex(1,1);
                      temp->add(*temp);
                      temp->printVal();
                      delete(temp);       //销毁temp内存
                      system("pause");
                      return 0;
                  }
                  

                  其中,->亦承自C语言,用于类指针调用类成员,其结果为

                  PS E:\Code\cpp> g++ .\oop.cpp
                  PS E:\Code\cpp> .\a.exe      
                  real part: 2
                  image part:2
                  请按任意键继续. . . 
                  

                  9.析构函数

                  一般通过new来分配内存空间,需要在调用结束之后使用delete对内存进行释放,delete的执行过程,便会调用析构函数。

                  在解释析构函数之前,需要回顾一下构造函数,所谓构造函数,即与类名相同的函数,通过构造函数可以创建一个类的对象,并开辟足够的内存。析构函数即销毁函数,将构造函数开辟的内存销毁掉。

                  析构函数亦与类名相同,而且无参数无return不可重载,是一个不易理解但易于使用的方法。

                  public:
                      explicit Complex(float inReal=0, float inIm=0);
                      //此即析构函数,
                      ~Complex(){}
                  

                  10.friend

                  在复数类中,实部和虚部被封装为私有变量,外部函数是无法访问的。此时,如果希望在其他类中创建一个提取复数实部或虚部的变量,则可以考虑友元机制。

                  所谓友元机制,即允许一个类将其非共有成员授权给指定的函数或者类,通过关键字friend修饰。例如,

                  /*
                  Complex类
                  */
                      friend float getReal(Complex num);
                  };
                  float getReal(Complex num){
                      cout<<num.real<<endl;
                      return num.real;
                  }
                  

                  这样,getReal就可以直接访问Complex类的私有成员。

                  11.类的继承

                  一般来说,复数 a + b i a+b\text{i} a+bi并不支持类似直乘的操作,即 ( a + b i ) ∗ ( a + b i ) ≠ a b + c d i (a+b\text{i})*(a+b\text{i})\not ={ab+cd\text{i}} (a+bi)∗(a+bi)​=ab+cdi,那么如果希望构造一种新的代数关系,使之既支持复数乘法,又可以直乘,那么就需要新建一个类,为了避免代码过于重复,这个类可以作为复数类的派生类而存在。

                  需要注意的一点是,此前所创建的Complex类默认成员为私有,所以其imreal对于子类而言是不可访问的。出于简单考虑,我们将class改为struct,这样其子类便可无痛调用。

                  //Complex类的派生类
                  class antiComplex : Complex{
                  public:
                      antiComplex(float inReal,float inIm){
                          real = inReal;
                          im = inIm;
                      };
                      void printVal();
                      antiComplex operator*(antiComplex);    
                  };
                  antiComplex antiComplex::operator*(antiComplex num){
                      return antiComplex{real*num.real,im*num.im};
                  }
                  //重写printVal函数
                  void antiComplex::printVal(){
                      cout<<"I'm antiComplex"<<endl;
                      cout<<"real part: "<<real<<endl
                          <<"image part:"<<im<<endl;
                  }
                  int main(){
                      antiComplex temp{1,2};
                      temp.printVal();
                      temp = temp*temp;
                      temp.printVal();
                      system("pause");
                      return 0;
                  }
                  

                  其结果为

                  PS E:\Code\cpp> .\a.exe      
                  I'm antiComplex
                  real part: 1
                  image part:2
                  I'm antiComplex
                  real part: 1
                  image part:4
                  请按任意键继续. . .
                  

                  在C++中有三种继承方式,分别是publicprivateprotected,一般默认为public继承,其特点是无法访问父类的私有成员;private继承则连公有成员和保护成员都无法访问;protected则允许其子类访问,但不允许子类的子类访问。

                  具体表现如下表所示,其读法为public成员在private继承时表现为private成员。

                  public继承 private继承 protected继承 public成员 public private protected private成员 private private private protected成员 protected private protected

                  12.多态

                  所谓多态就是多个子类继承一个基类时的差异性,例如,ComplexantiComplex都可以作为一种抽象数据结构的子类,毕竟二者只有在乘除法上表现不同。

                  #include<iostream>
                  using namespace std;
                  struct Abstract{
                      float real;
                      float im;
                      Abstract(float inReal, float inIm){
                          real = inReal;
                          im = inIm;
                      }
                      void printVal(){
                          cout<<"I'm Abstract"<<endl;
                      };
                      Abstract& multi(Abstract val){};
                  };
                  struct Complex:Abstract{
                      Complex(float inReal, float inIm)
                          :Abstract(inReal,inIm){}
                      void printVal();
                      Abstract& multi(Abstract val);
                  };
                  void Complex::printVal(){
                      cout<<"I'm Complex:"
                          <<real<<"+"<<im<<"i"<<endl;
                  }
                  Abstract& Complex::multi(Abstract val){
                      float temp = real*val.real - im*val.im;
                      im = real*val.real + im*val.im;
                      real = temp;
                      return *this;
                  }
                  struct antiComplex:Abstract{
                      antiComplex(float inReal, float inIm)
                          :Abstract(inReal,inIm){}
                      void printVal();
                      Abstract& multi(Abstract val);
                  };
                  void antiComplex::printVal(){
                      cout<<"I'm antiComplex:"
                          <<real<<"+"<<im<<"j"<<endl;
                  }
                  Abstract& antiComplex::multi(Abstract val){
                      real = real*val.real;
                      im = im*val.im;
                      return *this;
                  }
                  int main(){
                      Complex temp{1,2};
                      antiComplex antemp{1,2};
                      temp.multi(temp).multi(temp);
                      antemp.multi(antemp).multi(temp);
                      temp.printVal();
                      antemp.printVal();
                      system("pause");
                      return 0;
                  }
                  

                  其输出结果为

                  PS E:\Code\cpp> g++ .\oop.cpp
                  PS E:\Code\cpp> .\a.exe
                  I'm Complex:-3+5i
                  I'm antiComplex:1+4j
                  请按任意键继续. . . 
                  

                  可见这个结果是错的,原因在于multi函数的返回指针为Abstract类型,而基类中的multi为一个空函数,所以只执行一次multi。所以,如果子类再调用函数之后继续保持子类的方法就好了,这就需要使用关键字virtual

                  13.virtual

                  直接通过指针来说明virtual的功能比较合适。

                  struct Abstract{
                      float real;
                      float im;
                      Abstract(float inReal, float inIm){
                          real = inReal;
                          im = inIm;
                      }
                      void printVal(){
                          cout<<"I'm Abstract"<<endl;
                      };
                  };
                  struct Complex:Abstract{
                      Complex(float inReal, float inIm)
                          :Abstract(inReal,inIm){}
                      void printVal(){
                          cout<<"I'm Complex:"
                          <<real<<"+"<<im<<"i"<<endl;
                      }
                  };
                  int main(){
                      Abstract* a;
                      Complex temp{1,2};
                      a = &temp;
                      a->printVal();
                      system("pause");
                      return 0;
                  }
                  

                  其运行结果为

                  PS E:\Code\cpp> g++ .\oop.cpp
                  PS E:\Code\cpp> .\a.exe
                  I'm Abstract
                  

                  也就是说,虽然父类的指针指向了子类的对象,但最终指针指向的仍然是父类的函数。而如果在父类函数前加上virtual关键字,即将其改为

                  struct Abstract{
                      float real;
                      float im;
                      Abstract(float inReal, float inIm){
                          real = inReal;
                          im = inIm;
                      }
                      virtual void printVal(){
                          cout<<"I'm Abstract"<<endl;
                      };
                  };
                  

                  则其输出为

                  PS E:\Code\cpp> .\a.exe
                  I'm Complex:1+2i
                  

                  可见虚函数的作用是使得父类指针指向实际对象的方法。将父类的multi函数变为

                  virtual Abstract& multi(Abstract val){};
                  

                  则最终的输出结果为

                  PS E:\Code\cpp> .\a.exe
                  I'm Complex:-16+34i
                  I'm antiComplex:-16+136j
                  

                  看的不过瘾?

                  C++元编程初步

                  以上就是C++面向对象入门全面详解的详细内容,更多关于C++面向对象入门的资料请关注海外IDC网其它相关文章!

                  【文章来自:http://www.yidunidc.com/gfcdn.html 欢迎留下您的宝贵建议】