目录
三、访问修饰符:public、private、protected
[1. 忘了在类外实现时写类名::](#1. 忘了在类外实现时写类名::)
[2. 在private函数里访问不到外部变量(反过来也是)](#2. 在private函数里访问不到外部变量(反过来也是))
[3. 结构体和类的区别(面试爱问)](#3. 结构体和类的区别(面试爱问))
一、从一个日常需求开始
假设你要写一个程序管理图书。每本书有:书名、作者、价格。
用C的结构体,你可能会这样:
c
struct Book {
char title[100];
char author[50];
double price;
};
// 然后写一堆函数操作它
void printBook(struct Book* b) { ... }
void discount(struct Book* b, double rate) { ... }
没什么问题,但你要记住:哪些函数能改price?改的时候要不要校验(比如价格不能为负)?随着代码变多,这些"约定"很容易被忘记。
C++的类把数据和操作绑在一起,并且控制谁能访问什么。
二、定义你的第一个类
下面是一个最简单的Book类:
cpp
#include <iostream>
#include <string>
using namespace std;
class Book {
public:
string title;
string author;
double price;
void print() {
cout << "书名:" << title << endl;
cout << "作者:" << author << endl;
cout << "价格:" << price << "元" << endl;
}
void setPrice(double p) {
if (p >= 0) {
price = p;
} else {
cout << "价格不能为负数!" << endl;
}
}
};
然后这样使用:
cpp
int main() {
Book b;
b.title = "C++ Primer";
b.author = "Lippman";
b.setPrice(128.5);
b.print();
return 0;
}
你可能会说:"这不就是结构体里塞了几个函数吗?"------差不多,但多了控制权限。
三、访问修饰符:public、private、protected
上面的代码里出现了public:,它的意思是:这些东西外面可以直接访问。
C++提供了三个访问修饰符:
| 修饰符 | 谁能访问 | 日常理解 |
|---|---|---|
public |
任何人 | 公开的接口,像遥控器上的按钮 |
private |
只有类自己的成员函数 | 内部秘密,用户碰不到 |
protected |
自己 + 子类 | 可以传给后代,但外人不行 |
举个例子,看看区别:
cpp
class BankAccount {
public:
string owner; // 谁都能看账户名
void deposit(double money) {
if (money > 0) balance += money;
}
double getBalance() { return balance; }
private:
double balance; // 余额不能直接碰
};
int main() {
BankAccount acc;
acc.owner = "张三"; // ✅ 可以,owner是public
acc.deposit(500); // ✅ 可以,通过函数操作
acc.balance = 1000; // ❌ 编译错误!balance是private
cout << acc.getBalance(); // ✅ 只能通过函数获取
}
为什么要这样?
-
balance直接暴露在外面,万一有人写acc.balance = -10000就出问题了 -
通过
deposit()函数,我们可以加校验(只能存正数) -
通过
getBalance()只读不写(没有提供setBalance),余额就只能看不能改
封装的核心不是"藏起来",而是"可控"。
四、成员变量怎么声明?
成员变量可以像普通变量一样声明,但一般写在private或protected区域:
cpp
class Student {
private:
string name; // 字符串类型
int age; // 整型
double score[5]; // 数组
int* pData; // 指针(后面讲动态内存时会用)
public:
// 成员函数...
};
几条潜规则(不是语法强制,但建议遵守):
-
成员变量放在
private下------除非你有特殊理由 -
命名风格保持一致 ,很多人用下划线后缀:
name_、age_、m_name -
不要在类内给成员变量赋默认值(除非用C++11的类内初始化,后面会讲)
五、成员函数:两种实现方式
成员函数可以在类内直接写,也可以在类外写。
方式一:类内实现(隐式内联)
刚才的Book类就是类内实现:
cpp
class Book {
public:
void print() {
cout << title << endl; // 直接写在类里
}
};
优点 :简单直观
缺点:函数体暴露在头文件里,编译依赖重(改动函数实现会重新编译所有包含头文件的代码)
方式二:类外实现(推荐)
把声明和实现分开:
cpp
// Book.h 头文件
class Book {
public:
void print(); // 只声明
void setPrice(double p);
private:
string title;
string author;
double price;
};
// Book.cpp 实现文件
#include "Book.h"
#include <iostream>
using namespace std;
void Book::print() { // 注意 Book:: 表示这个函数属于Book类
cout << "书名:" << title << endl;
cout << "作者:" << author << endl;
cout << "价格:" << price << "元" << endl;
}
void Book::setPrice(double p) {
if (p >= 0) price = p;
}
Book::print()的::叫作用域运算符 ,意思是"print这个函数是Book这个类里的"。
类外实现的优点:
-
头文件只放接口,干净整洁
-
修改函数实现时,只编译
.cpp文件,不用重新编译所有依赖 -
多人协作时可以并行开发(一个人改.h,一个人改.cpp)
对于只有三五行的简单函数(比如
getBalance()),可以直接写类内;复杂的逻辑写类外。
六、一个完整的例子:时钟类
把前面知识点串起来,我们来写一个有实际意义的Clock类。
cpp
// Clock.h
#ifndef CLOCK_H // 防止重复包含
#define CLOCK_H
class Clock {
public:
void setTime(int h, int m, int s);
void tick(); // 走一秒
void display(); // 显示时间
private:
int hour;
int minute;
int second;
void normalize(); // 辅助函数,处理进位(只内部用,所以private)
};
#endif
cpp
// Clock.cpp
#include "Clock.h"
#include <iostream>
#include <iomanip>
using namespace std;
void Clock::setTime(int h, int m, int s) {
hour = h;
minute = m;
second = s;
normalize(); // 万一传进来的数值超出范围
}
void Clock::tick() {
second++;
normalize();
}
void Clock::normalize() {
if (second >= 60) {
minute += second / 60;
second %= 60;
}
if (minute >= 60) {
hour += minute / 60;
minute %= 60;
}
if (hour >= 24) {
hour %= 24;
}
}
void Clock::display() {
cout << setfill('0');
cout << setw(2) << hour << ":"
<< setw(2) << minute << ":"
<< setw(2) << second << endl;
}
cpp
// main.cpp
#include "Clock.h"
int main() {
Clock c;
c.setTime(23, 59, 55);
for (int i = 0; i < 10; i++) {
c.display();
c.tick();
}
return 0;
}
// 输出:
// 23:59:55
// 23:59:56
// ...
// 00:00:04
这个例子中:
-
hour、minute、second是private------外部不能随意篡改 -
setTime、tick、display是public------用户通过它们操作时钟 -
normalize是private------外部不需要知道"进位"的具体逻辑
七、常见错误(新手必踩的坑)
1. 忘了在类外实现时写类名::
cpp
void print() { ... } // ❌ 这是全局函数,不是Book类的
void Book::print() { ... } // ✅ 正确
2. 在private函数里访问不到外部变量(反过来也是)
成员函数可以访问本对象的任何成员(不管public还是private),但不是直接访问别的对象的私有成员。
3. 结构体和类的区别(面试爱问)
-
struct:成员默认是public -
class:成员默认是private
除此之外,在C++里几乎没有区别。很多人用struct表示纯数据容器(像C语言那样)。
八、这一篇的收获
你现在应该能:
-
用
class定义一个类 -
区分
public、private、protected的访问权限 -
在类内或类外实现成员函数
-
理解封装的第一步:把数据藏起来,提供接口函数
💡 小作业:定义一个
Rectangle类,有宽度和高度(private),提供setSize()、getArea()、getPerimeter()(public),并写一个main函数测试。
下一篇预告:第3篇《类与对象(二):构造函数与析构函数》------对象出生时自动执行的代码,和对象销毁时做的清理工作。你会发现,原来很多"初始化"和"收尾"的活儿,根本不用手动调用。