第7章:类(Classes)
一、导读
1.1 本章学习目标
第7章介绍C++面向对象编程的基础:类的定义和使用。本章核心目标:
- 掌握类的定义:数据成员和成员函数
- 理解访问控制:public、private、protected
- 掌握构造函数和析构函数:对象的初始化和清理
- 理解this指针:指向当前对象的指针
- 学会使用友元:友元函数和友元类
- 掌握类的声明和定义分离:头文件和源文件
1.2 为什么本章如此重要
类是C++面向对象编程的基石。理解类的设计原则,才能编写出结构清晰、可维护的C++代码。本章介绍的构造函数、析构函数、访问控制等概念,贯穿整个C++面向对象编程。
1.3 核心问题引导
构造函数和析构函数的调用顺序是什么?
为什么需要初始化列表?直接在构造函数体内赋值有什么问题?
const成员函数有什么作用?为什么getter通常应该是const的?
友元破坏了封装性,为什么C++还要提供这个特性?
二、核心概念详解
7.1 类的定义
cpp
#include <iostream>
#include <string>
class Student {
private:
std::string name;
int age;
float score;
public:
// 构造函数
Student(std::string n, int a, float s)
: name(n), age(a), score(s) {}
// 默认构造函数
Student() : name("Unknown"), age(0), score(0.0f) {}
// 成员函数
void display() const {
std::cout << "Name: " << name
<< ", Age: " << age
<< ", Score: " << score << std::endl;
}
// getter
std::string getName() const { return name; }
int getAge() const { return age; }
float getScore() const { return score; }
// setter
void setName(const std::string &n) { name = n; }
void setAge(int a) {
if (a > 0 && a < 150) age = a;
}
void setScore(float s) {
if (s >= 0 && s <= 100) score = s;
}
};
int main() {
Student s1("Alice", 20, 95.5f);
Student s2;
s2.setName("Bob");
s2.setAge(21);
s2.setScore(88.0f);
s1.display();
s2.display();
return 0;
}7.2 构造函数和析构函数
cpp
#include <iostream>
#include <string>
class Resource {
private:
std::string name;
public:
// 构造函数
Resource(std::string n) : name(n) {
std::cout << "Constructor: " << name << std::endl;
}
// 拷贝构造函数
Resource(const Resource &other) : name(other.name + "_copy") {
std::cout << "Copy constructor: " << name << std::endl;
}
// 移动构造函数(C++11)
Resource(Resource &&other) noexcept : name(std::move(other.name)) {
std::cout << "Move constructor: " << name << std::endl;
}
// 析构函数
~Resource() {
std::cout << "Destructor: " << name << std::endl;
}
void display() const {
std::cout << "Resource: " << name << std::endl;
}
};
int main() {
Resource r1("File1");
Resource r2 = r1; // 拷贝构造
Resource r3 = std::move(r1); // 移动构造
r1.display();
r2.display();
r3.display();
return 0;
}7.3 const成员函数和this指针
cpp
#include <iostream>
class Counter {
private:
int count;
public:
Counter() : count(0) {}
// const成员函数:不能修改成员变量
int getCount() const {
return count;
}
void display() const {
std::cout << "Count: " << count << std::endl;
}
// 非const成员函数:可以修改成员变量
void increment() {
count++;
}
// 使用this指针
Counter& setCount(int c) {
this->count = c;
return *this; // 支持链式调用
}
void reset() {
this->count = 0;
}
};
int main() {
Counter c;
c.increment();
c.increment();
c.display(); // Count: 2
// 链式调用
c.setCount(10).setCount(20);
c.display(); // Count: 20
// const对象只能调用const成员函数
const Counter cc;
std::cout << cc.getCount() << std::endl;
// cc.increment(); // 错误:const对象不能调用非const函数
return 0;
}7.4 友元
cpp
#include <iostream>
class Counter; // 前向声明
// 友元函数:可以访问类的私有成员
void reset(Counter &c);
class Counter {
private:
int count;
public:
Counter() : count(0) {}
void increment() { count++; }
int getCount() const { return count; }
// 友元函数声明
friend void reset(Counter &c);
// 友元类
friend class CounterDisplay;
// 友元运算符重载
friend std::ostream& operator<<(std::ostream &os, const Counter &c);
};
void reset(Counter &c) {
c.count = 0; // 友元函数可以访问私有成员
}
class CounterDisplay {
public:
void show(const Counter &c) {
std::cout << "Counter value: " << c.count << std::endl;
}
};
std::ostream& operator<<(std::ostream &os, const Counter &c) {
os << "Counter(" << c.count << ")";
return os;
}
int main() {
Counter c;
c.increment();
c.increment();
std::cout << c << std::endl; // Counter(2)
reset(c);
std::cout << c << std::endl; // Counter(0)
CounterDisplay display;
c.increment();
display.show(c); // Counter value: 1
return 0;
}7.5 类的声明和定义分离
cpp
// student.h - 头文件
#ifndef STUDENT_H
#define STUDENT_H
#include <string>
class Student {
private:
std::string name;
int age;
float score;
public:
Student();
Student(std::string n, int a, float s);
void display() const;
std::string getName() const;
void setName(const std::string &n);
float getScore() const;
void setScore(float s);
};
#endif
// student.cpp - 实现文件
#include "student.h"
#include <iostream>
Student::Student() : name("Unknown"), age(0), score(0.0f) {}
Student::Student(std::string n, int a, float s)
: name(n), age(a), score(s) {}
void Student::display() const {
std::cout << "Name: " << name
<< ", Age: " << age
<< ", Score: " << score << std::endl;
}
std::string Student::getName() const { return name; }
void Student::setName(const std::string &n) { name = n; }
float Student::getScore() const { return score; }
void Student::setScore(float s) {
if (s >= 0 && s <= 100) score = s;
}
// main.cpp - 主程序
#include "student.h"
int main() {
Student s("Alice", 20, 95.5f);
s.display();
return 0;
}三、实战案例
3.1 银行账户类
cpp
#include <iostream>
#include <string>
#include <stdexcept>
class BankAccount {
private:
std::string owner;
double balance;
public:
BankAccount(std::string o, double initial = 0.0)
: owner(o), balance(initial) {
if (initial < 0) {
throw std::invalid_argument("Initial balance cannot be negative");
}
}
void deposit(double amount) {
if (amount <= 0) {
throw std::invalid_argument("Deposit amount must be positive");
}
balance += amount;
}
void withdraw(double amount) {
if (amount <= 0) {
throw std::invalid_argument("Withdraw amount must be positive");
}
if (amount > balance) {
throw std::runtime_error("Insufficient funds");
}
balance -= amount;
}
double getBalance() const { return balance; }
std::string getOwner() const { return owner; }
void display() const {
std::cout << "Account: " << owner
<< ", Balance: $" << balance << std::endl;
}
friend std::ostream& operator<<(std::ostream &os, const BankAccount &acc) {
os << acc.owner << ": $" << acc.balance;
return os;
}
};
int main() {
BankAccount acc("Alice", 1000.0);
acc.display();
acc.deposit(500);
acc.withdraw(200);
acc.display();
std::cout << acc << std::endl;
try {
acc.withdraw(2000);
} catch (const std::runtime_error &e) {
std::cerr << "Error: " << e.what() << std::endl;
}
return 0;
}四、本章小结
第7章介绍了C++类的基础:类的定义、访问控制、构造函数/析构函数、const成员函数、this指针、友元、声明与定义分离。类是面向对象编程的基石。
五、思考与练习
思考题
构造函数初始化列表和构造函数体内赋值有什么性能差异?
为什么拷贝构造函数的参数必须是const引用?
移动语义解决了什么问题?
友元破坏了封装性,什么场景下必须使用友元?
编程练习
实现一个复数类(Complex),支持加减乘除和输出。
实现一个日期类(Date),支持日期运算和比较。
实现一个简单的字符串类(MyString),管理动态内存。