【c++面向对象编程】第2篇:类与对象(一):定义第一个类——成员变量与成员函数
目录一、从一个日常需求开始二、定义你的第一个类三、访问修饰符public、private、protected举个例子看看区别四、成员变量怎么声明五、成员函数两种实现方式方式一类内实现隐式内联方式二类外实现推荐六、一个完整的例子时钟类七、常见错误新手必踩的坑1. 忘了在类外实现时写类名::2. 在private函数里访问不到外部变量反过来也是3. 结构体和类的区别面试爱问八、这一篇的收获一、从一个日常需求开始假设你要写一个程序管理图书。每本书有书名、作者、价格。用C的结构体你可能会这样cstruct 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; } } };然后这样使用cppint 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自己 子类可以传给后代但外人不行举个例子看看区别cppclass 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区域cppclass Student { private: string name; // 字符串类型 int age; // 整型 double score[5]; // 数组 int* pData; // 指针后面讲动态内存时会用 public: // 成员函数... };几条潜规则不是语法强制但建议遵守成员变量放在private下——除非你有特殊理由命名风格保持一致很多人用下划线后缀name_、age_、m_name不要在类内给成员变量赋默认值除非用C11的类内初始化后面会讲五、成员函数两种实现方式成员函数可以在类内直接写也可以在类外写。方式一类内实现隐式内联刚才的Book类就是类内实现cppclass 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 }; #endifcpp// 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. 忘了在类外实现时写类名::cppvoid print() { ... } // ❌ 这是全局函数不是Book类的 void Book::print() { ... } // ✅ 正确2. 在private函数里访问不到外部变量反过来也是成员函数可以访问本对象的任何成员不管public还是private但不是直接访问别的对象的私有成员。3. 结构体和类的区别面试爱问struct成员默认是publicclass成员默认是private除此之外在C里几乎没有区别。很多人用struct表示纯数据容器像C语言那样。八、这一篇的收获你现在应该能用class定义一个类区分public、private、protected的访问权限在类内或类外实现成员函数理解封装的第一步把数据藏起来提供接口函数 小作业定义一个Rectangle类有宽度和高度private提供setSize()、getArea()、getPerimeter()public并写一个main函数测试。下一篇预告第3篇《类与对象二构造函数与析构函数》——对象出生时自动执行的代码和对象销毁时做的清理工作。你会发现原来很多“初始化”和“收尾”的活儿根本不用手动调用。