Skip to content

Latest commit

 

History

History
555 lines (477 loc) · 22.3 KB

类.md

File metadata and controls

555 lines (477 loc) · 22.3 KB

类的拷贝控制

构造函数、拷贝函数 、 赋值函数、析构函数

  class Sales_data{
  public:
    Sales_data() = default;// 默认合成 默认构造函数
    Sales_data(const Sales_data& ) =  default;// 默认合成 拷贝构造函数
    Sales_data& operator=(const Sales_data&);//  拷贝赋值运算符
    ~Sales_data() = default;// 默认合成 默认析构函数
  };

      Sales_data& Sales_data::operator=(const Sales_data&) = default;// 拷贝赋值运算符 类外定义

使用 delete 删除函数 避免拷贝

   struct NoCopy(){// struct 默认为 public
   NoCopy() = default;//  默认合成 默认构造函数    不能是删除函数
   NoCopy(const NoCopy&) = delete;// 阻止拷贝
   NoCopy&  operator=(const NoCopy&);//  阻止赋值
   ~NoCopy() = default;// 默认合成 默认析构函数   不能是删除函数
   };

private 拷贝控制  用户可定义和删除该对象 但不能拷贝它们   不过该类的成员函数 和其 有元函数和有元类 可以 拷贝

  class privateCopy{// class 默认为 private  普通用户无法访问
    privateCopy(const privateCopy&);// 私有拷贝构造函数
    privateCopy& operator=(const privateCopy&);// 私有拷贝赋值运算符
  public:
    privateCopy() = default; // 默认合成  默认构造函数
    ~privateCopy() = default;// 默认合成 默认析构函数   不能是删除函数

     };

 指针 与 拷贝构造函数 和 拷贝赋值运算符

  struct Node{
  char* name;//名字 字符指针
  int age;// 年龄
  Node(char* n = " ", int a = 0 ){//默认构造函数
   name = strdup(n);// strdup()字符串拷贝库函数
   age = a;
   }
   // 拷贝构造函数未定义     编译器自动生成一个(自动生成的 只是逐个对成员复制,遇到指针就会出问题)
   // 拷贝赋值运算符也未定义 编译器自动生成一个(自动生成的 只是逐个对成员复制,遇到指针就会出问题) 
  };

  // 执行拷贝时 指针对象会指向同一个对象
  Node node1("Role", 20), node2(node1);//使用node1 初始化 node2 
  strcpy(node2.name, "Wendy");// 指向了同一个对象
  node2.age = 30;// 非指针正常赋值
  cout << node1.name << " " << node1.age << " " << node2.name << " " << node2.age << endl;
  // Wendy  20  Wendy  30  // 

定义 拷贝构造函数

  struct Node{
  char* name;//名字 字符指针
  int age;// 年龄
  Node(char* n = " ", int a = 0 ){//默认构造函数
   name = strdup(n);// strdup()字符串拷贝库函数
   age = a;
   }
   // 拷贝构造函数
   Node(const Node& n){
   name = strdup(n.name);
   age = n.age;
   }
   // 拷贝赋值运算符也未定义 编译器自动生成一个  (自动生成的 只是逐个对成员复制,遇到指针就会出问题)
  };

  Node node1("Role", 20), node2(node1);//使用node1 初始化 node2  使用拷贝构造函数
  strcpy(node2.name, "Wendy");
  node2.age = 30;// 正常赋值
  cout << node1.name << " " << node1.age << " " << node2.name << " " << node2.age << endl;

     // Role 20 Wendy 30  //

定义拷贝赋值运算符  重载 赋值运算符

  struct Node{
  char* name;//名字 字符指针
  int age;// 年龄
  Node(char* n = " ", int a = 0 ){//默认构造函数
   name = strdup(n);// strdup()字符串拷贝库函数
   age = a;
   }
   
   // 拷贝构造函数
   Node(const Node& n){
   name = strdup(n.name);
   age = n.age;
   }
   
   // 拷贝赋值运算符
  Node& operate=(const Node& n){
      if(this != &n){// 确保赋值运算符的两边不是同一个类的同一个对象,避免重复 定义自己
         if(name != 0) free(name);//先释放之前的内存(如果之前已经指向了内存,避免内存泄漏)
         name = strdup(n.name);// 再用 等号 左边的对象初始化 对象
         age = n.age;
        }
      return  *this;// this 起始就是 类对象的地址
      }
  };

指针与析构函数  默认的析构函数 只会释放 指针所占据的内存 而不会释放指针指向的内存空间  会造成 内存泄漏

     struct Node{ char* name;//名字 字符指针 int age;// 年龄 Node(char* n = " ", int a = 0 ){//默认构造函数 name = strdup(n);// strdup()字符串拷贝库函数 age = a; }

   // 拷贝构造函数
   Node(const Node& n){
   name = strdup(n.name);
   age = n.age;
   }
   
   // 拷贝赋值运算符
  Node& operate=(const Node& n){
      if(this != &n){// 确保赋值运算符的两边不是同一个类的同一个对象,避免重复 定义自己
         if(name != 0) free(name);//先释放之前的内存(如果之前已经指向了内存,避免内存泄漏)
         name = strdup(n.name);// 再用 等号 左边的对象初始化 对象
         age = n.age;
        }
      return  *this;// this 起始就是 类对象的地址
      }

       // 定义析构函数       ~Node(){       if(name != 0) free(name);//指针不为空,就已经使用了指向的内存,需要free手动释放掉       }       };

通过函数返回引用类型  可以改变类内变量的访问方式   破坏了 隐藏域

  class refTest{
        public:
        int& getRefN(){
              return n;//n为类内私有变量 返回该私有变量引用后 类外就可以访问它
        }
        int getN(){
              return n;//n为类内私有变量
        }
        private:
              int n;
  } c;
  //c.n = 6;// 错误  外部直接访问不了 类内私有变量
  int& k = c.getRefN();//通过返回引用 可以间接访问修改  类内私有变量
  k = 7;//  类内私有变量 n 被修改为 7
  cout << c.getN();// 可以看到 n 被修改为 7
  c.getRefN() = 10;//直接通过 c.getRefN() 来给 类内私有变量 n 赋值

重载

重载输出运算符 <<  为特定类对象 重载输出运算符 返回的是 ostream& 输出流对象的引用

// 一般定义成 非成员函数 在{};外面
 ostream& operator<<(ostream& os, const Sales_data& item ){
   os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price();
   // 使用了 字符串的、int、double的 输出运算符 类构成类的 输出运算符
   return os;

    }

重载 输入 运算符 >>  返回的是 istream& 输入流对象的引用

  istream& operator>>(istream& is, const Sales_data& item ){
  double price;// 单价
  is >> item.bookNo >> item.units_sold >> price;
  if(is)//检测输入是否成功
    item.revenue = item.units_sold * price
  else
    item = Sales_data();//输入失败,对象呗赋予默认的状态
    return is;

     }

重载 关系运算符  ==  不等于也类似 !=

   // 关系运算符  返回类型为 bool  输入两个类对象的 常量 引用 避免拷贝 节省时间 常量限制 避免修改    bool operator==(const Sales_item &lhs, const Sales_item &rhs) {        // 在类中被是定义为 friend 有元函数 // friend bool operator==(const Sales_item&, const Sales_item&);// 重定向 等于符号 ==        return lhs.units_sold == rhs.units_sold && lhs.revenue == rhs.revenue && lhs.isbn() == rhs.isbn(); }

整体 浏览

    #ifndef SALESITEM_H
    // we're here only if SALESITEM_H has not yet been defined 
    #define SALESITEM_H

    // Definition of Sales_item class and related functions goes here
    #include <iostream>
    #include <string>

    class Sales_item {
    // these declarations are explained section 7.2.1, p. 270 
    // and in chapter 14, pages 557, 558, 561
    // 操作符 定义
    friend std::istream& operator>>(std::istream&, Sales_item&);// 重定向输入 >> 符号
    friend std::ostream& operator<<(std::ostream&, const Sales_item&);// 重定向输出 << 符号
    friend bool operator<(const Sales_item&, const Sales_item&);// 重定向 小于符号 < 
    friend bool operator==(const Sales_item&, const Sales_item&);// 重定向 等于符号 ==  
    public:
        // constructors are explained in section 7.1.4, pages 262 - 265
        // default constructor needed to initialize members of built-in type
        Sales_item() = default;
        Sales_item(const std::string &book): bookNo(book) { }
        Sales_item(std::istream &is) { is >> *this; }
    public:
        // operations on Sales_item objects
        // member binary operator: left-hand operand bound to implicit this pointer
        Sales_item& operator+=(const Sales_item&);

        // operations on Sales_item objects
        std::string isbn() const { return bookNo; }
        double avg_price() const;
    // private members as before
    private:
        std::string bookNo;      // implicitly initialized to the empty string
        unsigned units_sold = 0; // explicitly initialized
        double revenue = 0.0;
    };

    // used in chapter 10
    inline
    bool compareIsbn(const Sales_item &lhs, const Sales_item &rhs) 
    { return lhs.isbn() == rhs.isbn(); }

    // nonmember binary operator: must declare a parameter for each operand
    Sales_item operator+(const Sales_item&, const Sales_item&);

    inline bool 
    operator==(const Sales_item &lhs, const Sales_item &rhs)
    {
        // must be made a friend of Sales_item
        return lhs.units_sold == rhs.units_sold &&
               lhs.revenue == rhs.revenue &&
               lhs.isbn() == rhs.isbn();
    }

    inline bool 
    operator!=(const Sales_item &lhs, const Sales_item &rhs)
    {
        return !(lhs == rhs); // != defined in terms of operator==
    }

    // assumes that both objects refer to the same ISBN
    Sales_item& Sales_item::operator+=(const Sales_item& rhs) 
    {
        units_sold += rhs.units_sold; 
        revenue += rhs.revenue; 
        return *this;
    }

    // assumes that both objects refer to the same ISBN
    Sales_item 
    operator+(const Sales_item& lhs, const Sales_item& rhs) 
    {
        Sales_item ret(lhs);  // copy (|lhs|) into a local object that we'll return
        ret += rhs;           // add in the contents of (|rhs|) 
        return ret;           // return (|ret|) by value
    }

    std::istream& 
    operator>>(std::istream& in, Sales_item& s)
    {
        double price;
        in >> s.bookNo >> s.units_sold >> price;
        // check that the inputs succeeded
        if (in)
            s.revenue = s.units_sold * price;
        else 
            s = Sales_item();  // input failed: reset object to default state
        return in;
    }

    std::ostream& 
    operator<<(std::ostream& out, const Sales_item& s)
    {
        out << s.isbn() << " " << s.units_sold << " "
            << s.revenue << " " << s.avg_price();
        return out;
    }

    double Sales_item::avg_price() const
    {
        if (units_sold) 
            return revenue/units_sold; 
        else 
            return 0;
    }

       #endif

模板类

  template<class genType>//声明模板类型
  class genClass{
        genType storage[50];//模板类型 数组
        };
  // 定义
  genClass<int>    intObject;   // int 类型数组
  genClass<double> doubleObject;// double 类型数组

  // 其二也可以将 数组大小推迟定义
  template<class genType, int size = 50>// 声明模板类型 书序大小默认为50
  class genClass{
        genType storage[size];//模板类型 数组
        };
  // 定义
  genClass<int>    intObject1;         // int 类型数组 默认   大小
  genClass<int, 100>    intObject2;    // int 类型数组 数组大小为 100 
  genClass<double, 123> doubleObject;  // double 类型数组  数组大小为 123

模板函数

  // 交换两个变量的值
  template<class genType>//声明模板类型
  void my_swap(genType& num1, genType& num2){   // 注意为 引用 需要对实参修改
        genType temp = num1;
        num1 = num2;
        num2 = temp;
      }

     

类的 继承

  #include <iostream> 
  using namespace std;

  // 基类 
  class BaseClass {
  public://共有类型 哪里都可以访问 
  BaseClass() { }//默认构造函数 
        void f(char *s = "unknown") {
              cout << "Function f() in BaseClass called from " << s << endl;
            h();//类内可以访问 类的私有函数 
      }

  protected:// 保护类型  子类 和 友元函数/有元类 可以访问 
        void g(char *s = "unknown") {
              cout << "Function g() in BaseClass called from " << s << endl;
        }

  private:// 类内 和 元函数/有元类 可以访问 
        void h() {
              cout << "Function h() in BaseClass\n";
        }
  };

  // 子类 Derived1Level1 共有继承自 基类   BaseClass 
  class Derived1Level1 : public virtual BaseClass {
  public:
        void f(char *s = "unknown") {
              cout << "Function f() in Derived1Level1 called from " << s << endl;
              g("Derived1Level1");// 本子类没有重写g() 因此调用的是 父类的g() 保护类型可以继承 子类内可以访问 
              // 函数时调用的基类的 但是参数 还是按子类传入的  
              h("Derived1Level1");// 本子类重写了h()(本身父类的为私有,子类也调用不了), 因此调用的是 自己的 h() 
        }

        void h(char *s = "unknown") {
              cout << "Function h() in Derived1Level1 called from " << s << endl;
        }
  };


  class Derived2Level1 : public virtual BaseClass {
  public:
        void f(char *s = "unknown") {
        cout << "Function f() in Derived2Level1 called from " << s << endl;
        g("Derived2Level1");// 调用基类的g() 保护类型的函数  但是参数按 子类传入的参数  
        // h(); // 错误 : h()为基类的 私有函数 ,子类访问不了 BaseClass::h() is not accessible
        }
  };

  //二级继承 
  class DerivedLevel2 : public Derived1Level1, public Derived2Level1 {
  public:
        void f(char *s = "unknown") {
        cout << "Function f() in DerivedLevel2 called from " << s << endl;
        g("DerivedLevel2");// g()函数只有基类有 实现 调用的是基类的 保护继承g() 但是参数是 子类 最新传递的 
        Derived1Level1::h("DerivedLevel2");// 父亲Derived1Level1有h()  爷爷 也有h() 需要指定调用的哪一个  参数都是最新的 
        BaseClass::f("DerivedLevel2");// f()函数  两个父亲 和 一个爷爷都有实现,需要指明调用哪一个  参数都是最新的 
        }
  };


  int main() {
  BaseClass bc;//基类 
  Derived1Level1 d1l1;//子类1 
  Derived2Level1 d2l1;//子类2 
  DerivedLevel2 dl2;//子类的子类 

  bc.f("main(1)");// 
  // bc.g(); // 错误: 保护类型 类外访问不了 BaseClass::g() is not accessible
  // bc.h(); // 错误: 私有类型 类外访问不了 BaseClass::h() is not accessible
  cout << "---------------"<< endl; 

  d1l1.f("main(2)");//首先调用子类重写的 f() 
  // 内部调用了 基类的g()(函数时调用的基类的 但是参数 还是按子类传入的 ) 和 自身重写的 h() 
  // d1l1.g(); // 错误: 在子类内 可以访问父类的保护类型函数g(), 外面访问不了 BaseClass::g() is not accessible


  d1l1.h("main(3)");// 调用子类自己重写的函数 参数按最新传入的 

  d2l1.f("main(4)");// 调用子类的函数f(),内部调用了父类的b保护类型函数g() 但是参数按子类传入的参数 
  // d2l1.g(); // 父类的保护函数g() 在子类内可以访问 ,外部访问不了 错误 BaseClass::g() is not accessible
  // d2l1.h(); // 父类的私有函数 子类内,外部都访问不了 错误: BaseClass::h() is not accessible


  dl2.f("main(5)");//类自身f() 内部 爷爷的 g() 父亲1的 h() 爷爷的  f()  参数都是最新传入的 
  // dl2.g(); // error: BaseClass::g() is not accessible

  dl2.h();//外部能访问 父亲  Derived1Level1的 共有类型函数 h() 
  return 0;
  }
  /*
  Function f() in BaseClass called from main(1)
  Function h() in BaseClass
  Function f() in Derived1Level1 called from main(2)
  Function g() in BaseClass called from Derived1Level1
  Function h() in Derived1Level1 called from Derived1Level1
  Function h() in Derived1Level1 called from main(3)
  Function f() in Derived2Level1 called from main(4)
  Function g() in BaseClass called from Derived2Level1
  Function f() in DerivedLevel2 called from main(5)
  Function g() in BaseClass called from DerivedLevel2
  Function h() in Derived1Level1 called from DerivedLevel2
  Function f() in BaseClass called from DerivedLevel2
  Function h() in BaseClass
  Function h() in Derived1Level1 called from unknown

  --------------------------------
  Process exited with return value 0
  Press any key to continue . . .

  */ 

类的多态性  基类定义的虚函数,子类(其他类)可以重写,根据指针当前指向的对象类型动态调用属于哪一确定类的该虚函数

  多态性指的是获得多种形态的能力。
  在 OOP 中,多态性指的是用同样的函数名称表示多个函数,而这些函数是不同对象的成员。

  对于所谓的静态绑定来说,调用哪个函数是在编译阶段确定的。
  而对于动态绑定,则要推迟到运行阶段才能确定。
  在 C++中,动态绑定是通过将成员函数声明为 virtual 来实现的。在这种方式中,
  如果对虚函数成员进行调用,那么选择执行哪个函数

  并不依赖于声明的指针类型,而是依赖于指针当前指向的类型。

  #include <iostream>
  using namespace std;

  //类1 
  class Class1 {
  public:
  virtual void f() {//虚函数   支持动态绑定 运行时确定 
        cout << "Function f() in Class1\n";
  }
  void g() {// 静态绑定 在编译阶段确定 
        cout << "Function g() in Class1\n";
  }
  };

  // 类2 
  class Class2 {
  public:
  virtual void f() {//虚函数   支持动态绑定 运行时确定 
        cout << "Function f() in Class2\n";
  }
  void g() {
        cout << "Function g() in Class2\n";
  }
  };

  //类3 
  class Class3 {
  public:
  virtual void h() {//虚函数   支持动态绑定 运行时确定 
        cout << "Function h() in Class3\n";
  }
  };

  int main() {
  Class1 object1, *p;// 类1 对象 object1  指针 p 声明为 Class1*类型
  Class2 object2;// 类2 对象 object2
  Class3 object3;// 类3 对象 object3
  p = &object1;//当前p 指向  Class1类的  object1对象  
  p->f();// 动态绑定 到 Class1类的 f()虚函数     Function f() in Class1
  p->g();// 静态绑定 在编译阶段确定              Function g() in Class1
  p = (Class1*) &object2;// 当前p 指向  Class2类的  object2对象 
  p->f();// 动态绑定 到 Class2类的 f()虚函数     Function f() in Class2
  p->g();// 静态绑定 在编译阶段确定              Function g() in Class1
  p = (Class1*) &object3;// 当前p 指向  Class3类的  object3对象 
  p->f(); // 程序会结束 报错  Class3类 无虚函数  f()   devc测试 竟然输出了  Function h() in Class3
  p->g(); // 静态绑定 在编译阶段确定             Function g() in Class1
  //p->h(); // h() is not a member of Class1;
  return 0;
  }

  /* 
  该程序的输出如下所示:
  Function f() in Class1
  Function g() in Class1
  Function f() in Class2
  Function g() in Class1
  // 程序会结束    Class3类 无虚函数  f()   devc测试 竟然输出了  Function h() in Class3
  Function g() in Class1
  */

多态性  优点  

  多态性是 OOP 的一项强大功能。可以利用这个特性将一个标准消息发送给许多不同的对象,
  而不需要指定如何处理消息,也不需要知道对象是什么类型。接收者负责解释和处理消息。发送者
  不需要根据接收者的类型来修改消息,也不需要使用 switch 或 if-else 语句。此外还可以在复杂的程
  序中加入新的单元而不需要重新编译整个程序。

有元函数 有元类 拓展了C++隐藏特性

  class C {
  int n;//class 默认 属性为 provide 私有
  friend class B;//有元类
  friend int f();//有元函数
  } obc;

  int f(){
  return 10 * obc.n; // 函数f() 可以访问 类C的对象 obc 的所有类型的变量和方法
  }

  class B {// 函数B 可以访问 类C的对象 obc 的所有类型的变量和方法
  int m;
  int function(){
        return  obc.n;
   }
  }

函数运算符的重载 类   函数运算符 () operator()

  class classfuc{
  public:
        classfuc() {// 默认构造函数
        }
        double operator() (double x){// 重载函数运算符 ()
              return 2*x;
        }
        double dou(double x){//定义功能函数
              return 2*x
        }
  };

  //定义使用 重载了函数运算符的类 定义的函数
  double sum2(classfuc f, int n, int m){
        double res = 0.0;
        for (int i = n; i < = m; ++i){
              res += f(i); 
              // res += f.dou(i);//使用 功能函数
        }
        return res;
  }
  // 使用函数
  classfuc fc;
  cout << sum2(cf, 2, 10) << endl;