Bootstrap

C++学习简短笔记——类与对象

类与对象

(1)第一部分:类基础

【关系说明】

  • 类:同类对象的抽象模板(设计蓝图)

  • 对象:类的具体实例(根据蓝图建造的房子)

【访问控制】

class Clock {
private:    // 仅类内可访问(默认)
    int hour, minute, second;  
protected:  // 类内和派生类可访问
    string model;
public:     // 完全开放访问
    void setTime(int h, int m, int s);
    void display();
};

【对象定义与访问】

Clock myClock;          // 创建对象
myClock.setTime(10,30,0);  // 正确:访问public成员
// myClock.hour = 10;   // 错误:private成员外部不可访问

【成员函数实现】

  • 类外实现(推荐方式):

    // 函数原型在类内声明
    void Clock::setTime(int h, int m, int s) { 
        hour = h;  // 直接访问private成员
        minute = m;
        second = s;
    }
  • 内联函数实现(两种方式):

    // 方式1:类内直接定义(自动inline)
    class Clock {
    public:
        void display() {  // 自动成为内联函数
            cout << hour << ":" << minute << ":" << second;
        }
    };
    
    // 方式2:显式声明inline
    inline void Clock::reset() { 
        hour = minute = second = 0; 
    }
    (2)第二部分:高级特性

    【1. 构造函数】
    【作用】 对象初始化(类似给新建房屋通水电)
    【核心特性】

  • 函数名=类名,无返回值

  • 未定义时编译器生成默认空构造函数

  • 自定义后默认构造函数失效(可通过重载恢复)

    class Student {
    private:
        string name;
        int age;
    public:
        // 自定义构造函数
        Student(string n, int a) : name(n), age(a) {} 
    
        // 恢复默认构造(两种写法)
        Student() = default;  // C++11推荐写法
        // Student() {}       // 传统写法
    };
    
    // 使用示例
    Student s1("Alice", 18);  // 调用自定义构造
    Student s2;               // 调用默认构造

    【委托构造函数】

    class Point {
        int x, y;
    public:
        Point() : Point(0,0) {}    // 委托给双参构造
        Point(int a) : Point(a,0) {} 
        Point(int a, int b) : x(a), y(b) {}
    };

    【2. 复制构造函数】
    【形式】 类名(const 类名& 源对象)
    【调用场景】

    class Book {
    public:
        Book(const Book& other) {  // 自定义复制构造
            cout << "复制构造被调用!";
        }
    };
    
    // 场景1:对象初始化
    Book book1;
    Book book2 = book1;  // 触发复制构造
    
    // 场景2:函数传参
    void readBook(Book b) {...}
    readBook(book1);     // 传参时触发复制构造

    【禁用复制构造】

    Book(const Book&) = delete;  // 禁止复制

    【3. 析构函数】
    【作用】 对象销毁时自动调用(类似房屋拆除)

  • class FileHandler {
        FILE* file;
    public:
        FileHandler(const char* filename) { 
            file = fopen(filename, "r"); 
        }
        ~FileHandler() { 
            if(file) fclose(file);  // 确保资源释放
        }
    };

类的组合

1. 基本概念

核心特点:类中包含其他类的对象作为成员

class Engine {  // 组件类
public:
    Engine(string type) : type(type) {
        cout << "Engine构造: " << type << endl;
    }
private:
    string type;
};

class Car {     // 组合类
    Engine engine;  // 对象成员
    int wheels;
public:
    Car(string engType, int w) 
        : engine(engType), wheels(w) {  // 必须初始化对象成员
        cout << "Car构造完成" << endl;
    }
};
2. 构造函数设计原则

【重要原则】

  • 必须通过初始化列表对对象成员进行构造

  • 基本类型成员也可通过初始化列表赋值

    // 正确写法(通过初始化列表构造)
    Car(string engType, int w) : engine(engType), wheels(w) {}
    
    // 错误写法(对象成员不能在函数体内构造)
    Car(string engType, int w) { 
        engine = Engine(engType);  // 错误!此处会先调用默认构造
        wheels = w; 
    }
    3. 初始化次序【重点】

    严格遵循两个规则

  • 声明顺序:按照成员在类中的声明顺序初始化(与初始化列表顺序无关)

  • 构造阶段

    • 先执行所有成员的初始化(对象成员调用构造函数,基本类型赋值)

    • 最后执行构造函数体内的代码

      class Computer {
          CPU cpu;      // 第1个声明
          Memory mem;   // 第2个声明
      public:
          // 初始化列表顺序不影响实际构造顺序
          Computer() : mem(8), cpu("i7") {}  
          // 实际构造顺序:cpu → mem
      };

      验证示例

      class A {
      public:
          A() { cout << "A构造" << endl; }
      };
      class B {
      public:
          B() { cout << "B构造" << endl; }
      };
      
      class Test {
          A a;  // 先声明
          B b;  // 后声明
      public:
          Test() : b(), a() {  // 初始化列表顺序故意颠倒
              cout << "Test构造体" << endl; 
          }
      };
      
      /* 输出结果:
      A构造
      B构造
      Test构造体
      */
      4. 组合类的复制构造函数

      【关键点】 需要显式处理对象成员的拷贝

      class Person {
          Heart heart;  // 心脏对象成员
      public:
          // 自定义复制构造
          Person(const Person& other) : heart(other.heart) {
              // 其他成员的复制...
          }
          
          // 默认复制构造等价于:
          // Person(const Person& other) : heart(other.heart) {}
      };

      深度拷贝示例

      class Wheel {
          int* pressure;  // 指针成员
      public:
          Wheel(int p) : pressure(new int(p)) {}
          
          // 自定义复制构造(深拷贝)
          Wheel(const Wheel& other) : pressure(new int(*other.pressure)) {}
          
          ~Wheel() { delete pressure; }
      };
      
      class Bicycle {
          Wheel frontWheel;
          Wheel backWheel;
      public:
          // 组合类的复制构造会自动调用成员对象的复制构造
          Bicycle(const Bicycle& other)
              : frontWheel(other.frontWheel),  // 调用Wheel的复制构造
                backWheel(other.backWheel) {}
      };
      5. 前向引用声明

      应用场景:两个类互相包含对方指针/引用时

      // 前向声明(只能声明不能实例化)
      class ClassB;  
      
      class ClassA {
      public:
          void interact(ClassB& b);  // 只能使用引用/指针
      private:
          ClassB* bPtr; 
      };
      
      class ClassB {  // 实际定义
      public:
          void connect(ClassA& a);
      };

      使用限制

    • 不能创建该类的对象

    • 不能访问成员细节(直到类被完整定义)

      class ClassC;  // 前向声明
      
      void func() {
          // ClassC c;              // 错误!不完整类型
          // cout << sizeof(ClassC); // 错误!不知道大小
          ClassC* ptr;              // 允许声明指针
      }

      重点标注说明

    • 【必须掌握】构造函数/析构函数的定义与使用场景

    • 【易错点】组合类的成员初始化顺序取决于声明顺序

    • 【重要区别】复制构造与默认构造的触发条件

    • 【常用技巧】通过=default=delete控制特殊函数

         重点练习建议

  1. 实现一个BankAccount类:

    • private数据:账号、余额

    • public方法:存款、取款、查询

    • 构造函数:初始化账号和初始余额

  2. 实现组合类Car

    • 包含Engine类和Wheel类对象成员

    • 测试构造函数的执行顺序




简略版本:

类与对象
(1)第一部分:
关系:类是同一类对象的抽象,对象是类的某一特定实体
public、private、protected
对象定义的语法:
类名 对象名 例如:Clock myClock     在类外,使用对象名.成语名来访问public
使用void Clock::setTime
public中的一组函数是共享private中的成员数据的,不需要有返回值
类的成员函数:
一般是在类中给出函数的原型声明,在类外给出函数体的实现,并在函数名前使用类名来加以限定
也可在类中直接给出函数体,形成内联成员函数(不要有循环语句和switch语句) 使用inline

(2)第二部分
1.构造函数:定义对象时对其进行初始化
形式:函数名与类名相同。不能定义返回值类型、也不能有返回值,即return语句
   未写时自动生成一个默认构造函数:参数表为空的构造函数,全部参数都有默认值的构造函数
在写了构造函数后,可直接写出初始化时的对象
当自己写了构造函数时,编译不会对其进行默认构造函数,但可以函数重载,再写一个默认构造函数
"=default"
若自己已经写了,但是还是想编译器生成构造函数,可以写一个构造函数=default
Clock()=default

委托构造函数:使用类中的其他构造函数来构造函数、
复制构造函数:用已经存在的对象来初始化新的构造函数
形式:类名(const 类名 &对象名);
"=delete":提示编译器不要生成默认复制构造函数
复制构造函数被调用两种情况:
1.定义一个对象,以本类另一个对象作为初始值,发生复制构造;
2.函数的形参时类的对象,调用函数时,将使用实参对象初始化形参对象,发生复制构造
左值和右值
左值引用&,右值引用&&
move函数
移动构造函数 //了解即可

析构函数:不存在参数
形式:~Point()

类的组合:
类中的成员是另一个类的 对象
类组合的构造函数设计:
原则:不仅对本类的基本类型成员初始化,也要对对象成员初始化
形式:生成出来

构造组合类对象时的初始化次序
首先对构造函数初始化列表中列出的成员(包括基本类型成员和对象成员
进行初始化,初始化次序是成员在类体中定义的次序。
。成员对象构造函数调用顺序:按对象成员的声明顺序,先声明者先构造。
初始化列表中未出现的成员对象,调用用默认构造函数(即无形参的)初始化
处理完初始化列表之后,再执行构造函数的函数体。

组合类的复制构造函数:。。

当两个类相互引用时,使用前向引用声明
使用时只能使用被声明的符号
但若需要知道细节的时候,就不能使用向前引用声明

;