目录

第五章 类与对象

本章将详细介绍C++的面向对象编程基础——类与对象,包括类的定义、构造函数、析构函数、this指针等核心概念。

5.1 类的基本概念

5.1.1 类的定义

类是C++中实现面向对象编程的基础,是用户自定义的数据类型,包含数据成员和成员函数。

#include <iostream>
#include <string>
using namespace std;
 
// 类的定义
class Student {
// 访问控制修饰符
public:     // 公有成员,外部可访问
    // 成员函数(方法)
    void setName(string n) {
        name = n;
    }
 
    string getName() const {
        return name;
    }
 
    void setAge(int a) {
        if (a > 0 && a < 150) {
            age = a;
        }
    }
 
    int getAge() const {
        return age;
    }
 
    void displayInfo() const {
        cout << "Name: " << name << ", Age: " << age << endl;
    }
 
private:    // 私有成员,只能在类内部访问
    // 数据成员
    string name;
    int age;
};
 
int main() {
    // 创建对象
    Student stu1;
 
    // 使用成员函数
    stu1.setName("Alice");
    stu1.setAge(20);
    stu1.displayInfo();
 
    // 错误:无法直接访问私有成员
    // stu1.name = "Bob";
    // stu1.age = 25;
 
    return 0;
}

5.1.2 访问控制

访问修饰符 类内部 派生类 外部
———–——–——–——
public
protected
private
#include <iostream>
using namespace std;
 
class BankAccount {
public:
    // 公有接口
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }
 
    bool withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            return true;
        }
        return false;
    }
 
    double getBalance() const {
        return balance;
    }
 
protected:
    // 受保护成员,派生类可访问
    string accountType = "Standard";
 
private:
    // 私有数据,封装实现细节
    double balance = 0.0;
    string password;
 
    bool verifyPassword(const string& pwd) {
        return password == pwd;
    }
};

5.1.3 struct与class的区别

#include <iostream>
using namespace std;
 
// struct:默认public
struct Point {
    double x;  // 默认public
    double y;
 
    void move(double dx, double dy) {
        x += dx;
        y += dy;
    }
};
 
// class:默认private
class Point2 {
    double x;  // 默认private
    double y;
 
public:
    void set(double xVal, double yVal) {
        x = xVal;
        y = yVal;
    }
 
    void move(double dx, double dy) {
        x += dx;
        y += dy;
    }
};
 
int main() {
    Point p1;
    p1.x = 10;  // 可以直接访问
    p1.y = 20;
 
    Point2 p2;
    // p2.x = 10;  // 错误!x是private
    p2.set(10, 20);  // 必须通过公有接口
 
    return 0;
}

5.2 构造函数

5.2.1 默认构造函数

#include <iostream>
#include <string>
using namespace std;
 
class Person {
public:
    // 默认构造函数(无参数)
    Person() {
        name = "Unknown";
        age = 0;
        cout << "Default constructor called" << endl;
    }
 
    // 带参数的构造函数
    Person(string n, int a) {
        name = n;
        age = a;
        cout << "Parameterized constructor called" << endl;
    }
 
    void display() const {
        cout << "Name: " << name << ", Age: " << age << endl;
    }
 
private:
    string name;
    int age;
};
 
int main() {
    Person p1;              // 调用默认构造函数
    Person p2("Alice", 20); // 调用带参数的构造函数
    Person p3 = Person("Bob", 25);  // 显式调用
    Person p4{"Charlie", 30};       // 列表初始化(C++11)
 
    p1.display();
    p2.display();
    p3.display();
    p4.display();
 
    return 0;
}

5.2.2 构造函数初始化列表

#include <iostream>
using namespace std;
 
class Member {
public:
    Member(int v) : value(v) {
        cout << "Member constructed with " << value << endl;
    }
private:
    int value;
};
 
class Container {
public:
    // 使用初始化列表
    Container(int a, int b, int c) 
        : x(a), y(b), z(c), member(a) {
        // 初始化列表中初始化成员
        // 比构造函数体内赋值效率更高
    }
 
    // 错误的做法(不能用于const成员和引用成员)
    // Container(int a, int b, int c) {
    //     x = a;  // 这是赋值,不是初始化
    //     y = b;
    //     z = c;
    // }
 
private:
    int x, y, z;
    Member member;  // 没有默认构造函数的成员
    const int CONST_VAL = 100;  // const成员必须在初始化列表中初始化
    int& ref;  // 引用成员必须在初始化列表中初始化
};
 
int main() {
    Container c(1, 2, 3);
    return 0;
}

5.2.3 委托构造函数(C++11)

#include <iostream>
#include <string>
using namespace std;
 
class Rectangle {
public:
    // 主构造函数
    Rectangle(double w, double h, string c) 
        : width(w), height(h), color(c) {
        cout << "Main constructor" << endl;
    }
 
    // 委托构造函数
    Rectangle() : Rectangle(1.0, 1.0, "white") {
        cout << "Delegating constructor (default)" << endl;
    }
 
    Rectangle(double w, double h) : Rectangle(w, h, "white") {
        cout << "Delegating constructor (no color)" << endl;
    }
 
    Rectangle(double side) : Rectangle(side, side, "white") {
        cout << "Delegating constructor (square)" << endl;
    }
 
    void display() const {
        cout << width << "x" << height << " " << color << endl;
    }
 
private:
    double width, height;
    string color;
};
 
int main() {
    Rectangle r1;           // 默认
    Rectangle r2(3, 4);     // 无颜色
    Rectangle r3(5);        // 正方形
    Rectangle r4(2, 3, "red");  // 全参数
 
    r1.display();
    r2.display();
    r3.display();
    r4.display();
 
    return 0;
}

5.2.4 拷贝构造函数

#include <iostream>
#include <cstring>
using namespace std;
 
class String {
public:
    // 普通构造函数
    String(const char* str = "") {
        size = strlen(str);
        data = new char[size + 1];
        strcpy(data, str);
        cout << "Constructor: " << data << endl;
    }
 
    // 拷贝构造函数
    String(const String& other) {
        size = other.size;
        data = new char[size + 1];
        strcpy(data, other.data);
        cout << "Copy constructor: " << data << endl;
    }
 
    // 析构函数
    ~String() {
        cout << "Destructor: " << (data ? data : "null") << endl;
        delete[] data;
    }
 
    void display() const {
        cout << data << endl;
    }
 
private:
    char* data;
    size_t size;
};
 
// 拷贝构造函数被调用的情况
String createString() {
    String s("Hello");
    return s;  // 可能触发拷贝构造(取决于优化)
}
 
void takeStringByValue(String s) {
    s.display();
}
 
int main() {
    String s1("World");      // 普通构造
    String s2 = s1;          // 拷贝构造
    String s3(s1);           // 拷贝构造(显式)
 
    takeStringByValue(s1);   // 拷贝构造(传递参数)
 
    // String s4 = createString();  // 可能触发拷贝构造
 
    return 0;
}

5.3 析构函数

5.3.1 析构函数基础

#include <iostream>
using namespace std;
 
class Resource {
public:
    Resource() {
        data = new int[100];
        cout << "Resource acquired" << endl;
    }
 
    // 析构函数
    ~Resource() {
        delete[] data;
        cout << "Resource released" << endl;
    }
 
    void doWork() {
        cout << "Working..." << endl;
    }
 
private:
    int* data;
};
 
void functionScope() {
    Resource r;  // 构造
    r.doWork();
    // r在这里超出作用域,自动调用析构函数
}
 
int main() {
    cout << "Entering main" << endl;
 
    functionScope();
 
    cout << "Creating dynamic object" << endl;
    Resource* ptr = new Resource();
    ptr->doWork();
    delete ptr;  // 必须手动delete才能调用析构函数
 
    cout << "Leaving main" << endl;
    return 0;
}

5.3.2 析构函数的调用时机

#include <iostream>
using namespace std;
 
class Demo {
public:
    Demo(int id) : id(id) {
        cout << "Constructor: " << id << endl;
    }
    ~Demo() {
        cout << "Destructor: " << id << endl;
    }
private:
    int id;
};
 
// 全局对象
Demo globalObj(1);  // 最先构造,最后析构
 
int main() {
    cout << "--- Enter main ---" << endl;
 
    Demo localObj(2);  // main中构造
 
    {
        cout << "--- Enter block ---" << endl;
        Demo blockObj(3);  // 块中构造
        cout << "--- Leave block ---" << endl;
        // blockObj在这里析构
    }
 
    static Demo staticObj(4);  // 只构造一次,程序结束时析构
 
    Demo* ptr = new Demo(5);   // 动态对象
    delete ptr;  // 手动析构
 
    cout << "--- Leave main ---" << endl;
    // localObj在这里析构
    return 0;
}
 
// staticObj在这里析构
// globalObj在这里析构(最后)

5.4 this指针

5.4.1 this指针的基础

#include <iostream>
using namespace std;
 
class Counter {
public:
    Counter(int count = 0) : count(count) {}
 
    // this指针指向调用成员函数的对象
    Counter& increment() {
        this->count++;  // 等价于 count++;
        return *this;   // 返回当前对象的引用
    }
 
    Counter& add(int value) {
        count += value;
        return *this;
    }
 
    // 解决命名冲突
    void setValue(int count) {
        this->count = count;  // 成员变量 = 参数
    }
 
    int getCount() const {
        return count;
    }
 
private:
    int count;
};
 
int main() {
    Counter c(0);
 
    // 链式调用
    c.increment().increment().add(5);
    cout << "Count: " << c.getCount() << endl;  // 7
 
    return 0;
}

5.4.2 this指针的应用

#include <iostream>
using namespace std;
 
class Node {
public:
    Node(int val) : value(val), next(nullptr) {}
 
    // 返回this实现链式插入
    Node* append(int val) {
        Node* newNode = new Node(val);
        Node* current = this;
        while (current->next != nullptr) {
            current = current->next;
        }
        current->next = newNode;
        return this;
    }
 
    void display() const {
        const Node* current = this;
        while (current != nullptr) {
            cout << current->value << " -> ";
            current = current->next;
        }
        cout << "null" << endl;
    }
 
    // 比较对象
    bool isSameObject(const Node& other) const {
        return this == &other;
    }
 
private:
    int value;
    Node* next;
};
 
int main() {
    Node* head = new Node(1);
    head->append(2)->append(3)->append(4);
 
    head->display();  // 1 -> 2 -> 3 -> 4 -> null
 
    Node n1(1), n2(1);
    cout << "Same object? " << n1.isSameObject(n2) << endl;  // 0
    cout << "Same object? " << n1.isSameObject(n1) << endl;  // 1
 
    return 0;
}

5.5 静态成员

5.5.1 静态数据成员

#include <iostream>
using namespace std;
 
class Student {
public:
    Student(string n) : name(n) {
        count++;
        id = count;
        cout << "Student " << name << " created, total: " << count << endl;
    }
 
    ~Student() {
        count--;
        cout << "Student " << name << " destroyed, total: " << count << endl;
    }
 
    static int getCount() {
        return count;
    }
 
    void display() const {
        cout << "ID: " << id << ", Name: " << name << endl;
    }
 
private:
    string name;
    int id;
    static int count;  // 静态成员声明
};
 
// 静态成员定义和初始化
int Student::count = 0;
 
int main() {
    cout << "Initial count: " << Student::getCount() << endl;
 
    Student s1("Alice");
    Student s2("Bob");
 
    cout << "Current count: " << Student::getCount() << endl;
 
    {
        Student s3("Charlie");
        cout << "Count in block: " << Student::getCount() << endl;
    }
 
    cout << "Final count: " << Student::getCount() << endl;
 
    return 0;
}

5.5.2 静态成员函数

#include <iostream>
using namespace std;
 
class MathUtils {
public:
    // 静态成员函数
    static int max(int a, int b) {
        return (a > b) ? a : b;
    }
 
    static int min(int a, int b) {
        return (a < b) ? a : b;
    }
 
    static double circleArea(double radius) {
        return PI * radius * radius;
    }
 
    // 静态成员函数不能访问非静态成员
    // static void badFunc() {
    //     nonStaticVar = 10;  // 错误!
    // }
 
private:
    static constexpr double PI = 3.14159;
    int nonStaticVar = 0;
};
 
// constexpr静态成员可以在类内初始化
double MathUtils::PI;  // 在类外定义(C++17前需要)
 
int main() {
    // 通过类名调用静态成员函数
    cout << "Max: " << MathUtils::max(10, 20) << endl;
    cout << "Min: " << MathUtils::min(10, 20) << endl;
    cout << "Area: " << MathUtils::circleArea(5.0) << endl;
 
    // 也可以通过对象调用(不推荐)
    MathUtils mu;
    cout << "Max: " << mu.max(30, 40) << endl;
 
    return 0;
}

5.6 const成员函数

#include <iostream>
using namespace std;
 
class Date {
public:
    Date(int y, int m, int d) : year(y), month(m), day(d) {}
 
    // const成员函数:不修改对象状态
    int getYear() const {
        // year = 2024;  // 错误!不能修改成员
        return year;
    }
 
    int getMonth() const { return month; }
    int getDay() const { return day; }
 
    void setYear(int y) {
        year = y;  // 非const函数可以修改成员
    }
 
    // const成员函数可以调用其他const成员函数
    void display() const {
        cout << year << "-" << month << "-" << day << endl;
        // setYear(2024);  // 错误!不能调用非const函数
    }
 
    // mutable成员:即使在const函数中也能修改
    void incrementAccessCount() const {
        accessCount++;  // 合法,因为accessCount是mutable
    }
 
private:
    int year, month, day;
    mutable int accessCount = 0;  // 可变成员
};
 
int main() {
    Date d1(2024, 1, 15);
    const Date d2(2024, 6, 1);
 
    d1.setYear(2025);  // 合法
    // d2.setYear(2025);  // 错误!const对象不能调用非const函数
 
    d1.display();  // 合法
    d2.display();  // 合法(display是const函数)
 
    d2.incrementAccessCount();  // 合法(修改mutable成员)
 
    return 0;
}

5.7 友元

5.7.1 友元函数

#include <iostream>
using namespace std;
 
class Box {
public:
    Box(double w, double h, double d) 
        : width(w), height(h), depth(d) {}
 
    // 声明友元函数
    friend double calculateVolume(const Box& box);
    friend class BoxManager;  // 友元类
 
    // 声明另一个类的成员函数为友元
    friend void externalFunction(const Box& box);
 
private:
    double width, height, depth;
};
 
// 友元函数可以访问私有成员
double calculateVolume(const Box& box) {
    return box.width * box.height * box.depth;
}
 
// 友元类
class BoxManager {
public:
    void resize(Box& box, double factor) {
        // 可以访问Box的私有成员
        box.width *= factor;
        box.height *= factor;
        box.depth *= factor;
    }
 
    double getSurfaceArea(const Box& box) {
        return 2 * (box.width * box.height + 
                    box.height * box.depth + 
                    box.width * box.depth);
    }
};
 
int main() {
    Box box(3, 4, 5);
    cout << "Volume: " << calculateVolume(box) << endl;
 
    BoxManager manager;
    cout << "Surface Area: " << manager.getSurfaceArea(box) << endl;
 
    manager.resize(box, 2);
    cout << "New Volume: " << calculateVolume(box) << endl;
 
    return 0;
}

5.8 本章小结

本章我们学习了:

练习题

基础练习:

1. 设计一个Book类,包含书名、作者、价格,提供相应的getter和setter

2. 实现一个计数器类,使用静态成员统计创建的对象数量

3. 编写一个字符串类MyString,实现构造函数和析构函数,管理动态内存

4. 实现一个BankAccount类,支持存款、取款、查询余额,确保余额不为负

进阶练习:

5. 设计一个矩阵类Matrix,支持基本的矩阵运算

6. 实现一个单向链表类LinkedList,包含插入、删除、查找操作

7. 创建一个日期类Date,支持日期计算(加减天数)

思考题:

8. 什么时候应该使用explicit关键字?举例说明

9. 分析以下代码的问题:

 <code cpp>
 String func() {
     String s("Hello");
     return s;
 }
 int main() {
     String s2 = func();
 }
 </code>

10. 比较深拷贝和浅拷贝,什么时候需要自定义拷贝构造函数?

参考答案

练习2参考:

class Counter {
private:
    static int count;
    int id;
public:
    Counter() {
        count++;
        id = count;
    }
    ~Counter() { count--; }
    static int getCount() { return count; }
    int getId() const { return id; }
};
int Counter::count = 0;

练习4参考:

class BankAccount {
private:
    string accountNumber;
    double balance;
public:
    BankAccount(string num, double bal = 0) 
        : accountNumber(num), balance(bal) {}
 
    void deposit(double amount) {
        if (amount > 0) balance += amount;
    }
 
    bool withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            return true;
        }
        return false;
    }
 
    double getBalance() const { return balance; }
};

下一章

继续学习:第六章 继承与多态