Bootstrap

C++学习:类和对象(一)

一、面向过程与面向对象编程

1. 什么是面向过程编程?

面向过程编程(Procedural Programming)是一种以过程(或函数)为中心的编程范式。程序被视为一系列按顺序执行的步骤,主要通过函数对数据进行操作

特点:

  • 执行顺序明确:程序按照代码书写的顺序执行
  • 侧重算法:重视具体的操作步骤和实现流程
  • 代码重用性低:相似的功能需要重复编写代码

代码示例:计算数组元素的平均值

#include <iostream>
using namespace std;

int main() {
    const int SIZE = 5;
    int numbers[SIZE] = {5, 10, 15, 20, 25};
    int sum = 0;

    for (int i = 0; i < SIZE; ++i) {
        sum += numbers[i];
    }

    double average = static_cast<double>(sum) / SIZE;
    cout << "平均值是:" << average << endl;

    return 0;
}

2. 什么是面向对象编程?

面向对象编程(Object-Oriented Programming,OOP)是一种以对象为中心的编程范式。它将数据和操作数据的函数封装在一起,称为对象。程序被设计为一组相互交互的对象

特点:

  • 封装(Encapsulation):将数据和相关操作封装在对象内部,保护数据不被外部直接访问
  • 继承(Inheritance):可以从已有的类创建新的类,重用代码
  • 多态(Polymorphism):不同对象可以以统一的方式被使用

代码示例:定义一个计算平均值的类

#include <iostream>
#include <vector>
using namespace std;

class Statistics {
private:
    vector<int> data;

public:
    void addData(int value) {
        data.push_back(value);
    }

    double calculateAverage() {
        if (data.empty()) return 0.0;
        int sum = 0;
        for (int val : data) {
            sum += val;
        }
        return static_cast<double>(sum) / data.size();
    }
};

int main() {
    Statistics stats;
    stats.addData(5);
    stats.addData(10);
    stats.addData(15);
    stats.addData(20);
    stats.addData(25);

    cout << "平均值是:" << stats.calculateAverage() << endl;

    return 0;
}

3. 比较一下:

 

二、引入与定义

1. 为什么需要类

面向对象编程中,类(Class)是创建对象的蓝图或模板。它定义了对象的属性(数据)和方法(行为)。通过类,可以创建多个具有相同属性和行为的对象

2. 类的定义

在C++中,类的定义使用class关键字

语法格式:

class 类名 {
    访问限定符:
        // 属性(成员变量)
        // 方法(成员函数)
};

代码示例:定义一个表示学生信息的类

#include <iostream>
using namespace std;

class Student {
public:
    // 属性
    string name;
    int age;
    double score;

    // 方法
    void introduce() {
        cout << "姓名:" << name << ", 年龄:" << age << ", 分数:" << score << endl;
    }
};

3. 类的成员

  • 成员变量(属性):保存对象的状态或数据,如nameagescore
  • 成员函数(方法):定义对象的行为或操作,如introduce()

三、访问限定符与封装

1. 访问限定符(入门篇也有)

访问限定符用于控制类的成员变量和成员函数的可见性。C++提供了三个访问限定符:

  • public(公有):公共成员,可以被类的内部和外部访问
  • private(私有):私有成员,只能被类的内部访问,外部不能直接访问
  • protected(受保护):受保护成员,可以被类的内部和子类访问,但无法被外部直接访问

2. 封装(Encapsulation)

封装是面向对象的基本特性之一,它将数据(属性)和操作数据的函数(方法)包装在一起,并对数据的访问进行控制,保护对象的完整性

3. 使用private进行封装

将类的成员变量设置为private,通过public的成员函数(如setget方法)来访问和修改数据

代码示例:

#include <iostream>
using namespace std;

class BankAccount {
private:
    string accountNumber;
    double balance;

public:
    // 构造函数
    BankAccount(string accNum, double bal) {
        accountNumber = accNum;
        balance = bal;
    }

    // 存款
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            cout << "存款成功,当前余额:" << balance << endl;
        } else {
            cout << "存款金额必须大于0。" << endl;
        }
    }

    // 取款
    void withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            cout << "取款成功,当前余额:" << balance << endl;
        } else {
            cout << "取款金额不合法。" << endl;
        }
    }

    // 查询余额
    double getBalance() {
        return balance;
    }
};

int main() {
    BankAccount account("1234567890", 1000.0);
    account.deposit(500.0);
    account.withdraw(200.0);
    cout << "最终余额:" << account.getBalance() << endl;

    // account.balance = 5000.0; // 错误,无法访问private成员

    return 0;
}

4. 访问限定符的使用注意

  • 默认访问级别:如果未指定访问限定符,class中的成员默认是privatestruct中的成员默认是public
  • 建议:将成员变量设置为privateprotected,通过public成员函数访问,增强封装性

四、类的作用域

1. 类的成员作用域

  • 类的内部:可以访问所有成员,包括privateprotectedpublic
  • 派生类(子类):可以访问基类的protectedpublic成员,不能访问private成员
  • 类的外部:只能访问对象的public成员,不能访问privateprotected成员

2. 作用域解析符::

作用域解析符::用于指定变量或函数所属的作用域

代码示例:

#include <iostream>
using namespace std;

class Circle {
private:
    double radius;

public:
    Circle(double r);

    double getArea();
};

// 构造函数的实现
Circle::Circle(double r) {
    radius = r;
}

// 成员函数的实现
double Circle::getArea() {
    return 3.14159 * radius * radius;
}

int main() {
    Circle c(5.0);
    cout << "圆的面积是:" << c.getArea() << endl;
    return 0;
}

五、对象的实例化

1. 什么是实例化?

实例化(Instantiation)是根据类创建对象的过程。对象是类的具体实例,拥有类定义的属性和方法

2. 实例化对象的方法

  • 方式一:直接声明对象
ClassName objectName;
  • 方式二:使用new关键字在堆上创建对象,返回指针
ClassName* objectPtr = new ClassName();

代码实例:方式一

#include <iostream>
using namespace std;

class Rectangle {
public:
    double width;
    double height;

    double getArea() {
        return width * height;
    }
};

int main() {
    Rectangle rect; // 实例化对象
    rect.width = 10.0;
    rect.height = 5.0;
    cout << "矩形的面积是:" << rect.getArea() << endl;
    return 0;
}

代码示例:方式二

#include <iostream>
using namespace std;

class Rectangle {
public:
    double width;
    double height;

    Rectangle(double w, double h) {
        width = w;
        height = h;
    }

    double getArea() {
        return width * height;
    }
};

int main() {
    Rectangle* rectPtr = new Rectangle(10.0, 5.0); // 使用指针
    cout << "矩形的面积是:" << rectPtr->getArea() << endl;

    delete rectPtr; // 释放内存
    return 0;
}

 

六、对象大小的计算

1. 对象的内存布局

对象的大小与其成员变量有关(静态成员除外)。成员函数存在于代码段,多个对象共享同一份函数代码,因此不计入对象大小

2. 计算对象大小的方式

使用sizeof操作符可以获取对象或类的大小

语法:

sizeof(object);
sizeof(ClassName);

代码示例:

#include <iostream>
using namespace std;

class Example {
    char a;      // 1字节
    int b;       // 4字节
    double c;    // 8字节
public:
    void func() {}
};

int main() {
    Example e;
    cout << "对象e的大小:" << sizeof(e) << " 字节" << endl;
    cout << "类Example的大小:" << sizeof(Example) << " 字节" << endl;
    return 0;
}

注意:

  • 由于内存对齐,实际大小可能大于成员变量大小之和
  • 成员函数不影响对象大小

4. 分析内存对齐

#include <iostream>
using namespace std;

class Alignment {
    char a;    // 1字节
    // padding 3字节,使下一个int在4字节边界上
    int b;     // 4字节
};

int main() {
    cout << "类Alignment的大小:" << sizeof(Alignment) << " 字节" << endl;
    return 0;
}

解释:

  • char a占1字节,为了对齐,下一个int从第4字节开始,需要填充3字节
  • 总大小为1 + 3(填充) + 4 = 8字节

七、this指针

1. 什么是this指针?

this指针是C++中类的成员函数内的一个隐含指针,指向调用该成员函数的对象本身。通过this指针,可以在成员函数中访问对象的成员变量和其他成员函数

2. this指针的特点

  • 隐式存在:不需要显式声明,可直接使用
  • 指向当前对象:用于区分成员变量和形参或局部变量
  • 常用在返回对象自身或链式调用中

 代码示例

1.区分成员变量和形参

#include <iostream>
using namespace std;

class Person {
private:
    string name;
    int age;

public:
    void setName(string name) {
        this->name = name; // 使用this指针
    }

    void setAge(int age) {
        this->age = age;
    }

    void display() {
        cout << "姓名:" << name << ", 年龄:" << age << endl;
    }
};

int main() {
    Person person;
    person.setName("Bob");
    person.setAge(25);
    person.display();
    return 0;
}

2.返回对象自身,实现链式调用

#include <iostream>
using namespace std;

class Calculator {
private:
    int result;

public:
    Calculator() : result(0) {}

    Calculator& add(int value) {
        result += value;
        return *this;
    }

    Calculator& subtract(int value) {
        result -= value;
        return *this;
    }

    Calculator& multiply(int value) {
        result *= value;
        return *this;
    }

    int getResult() {
        return result;
    }
};

int main() {
    Calculator calc;
    int finalResult = calc.add(10).subtract(5).multiply(3).getResult();
    cout << "计算结果是:" << finalResult << endl;
    return 0;
}

3. const成员函数中的this指针

const成员函数中,this指针的类型为const ClassName*,表示不能修改对象的成员变量

#include <iostream>
using namespace std;

class Sample {
private:
    int value;

public:
    Sample(int v) : value(v) {}

    void display() const {
        // this指针的类型为 const Sample*
        cout << "值是:" << value << endl;
        // this->value = 10; // 错误,不能修改成员变量
    }
};

int main() {
    Sample s(5);
    s.display();
    return 0;
}

;