C++学习笔记(6)
面向对象编程:
1.对象和类
(1)声明类: 类定义了对象的属性和行为。
一个对象的状态: 用数据域以及他们的当前值来表示。
一个对象的行为,由一组函数确定。
所以在c++类中,用变量定义数据域,用函数来定义行为
(2)定义类和创建对象:
定义一个圆的类:
#include <iostream> using namespace std; class Circle { public: double radius; // constructor Circle() { radius = 1.0; } // 构造方法 Circle(double new_radius) { radius = new_radius; } // 函数 double getArea() { return 3.14 * radius * radius; } }; // 这里要写分号 int main(int argc, char *argv[]) { Circle circle1; Circle circle2(12.5); Circle circle3(123); cout << "radius of circle 1 is " << circle1.radius << endl; cout << "Area of circle 3 is " << circle3.getArea() << endl; return 0; }
public关键字:表示所有的数据域,构造函数和普通成员函数都可以通过类对象来访问。如果不用关键字public,那么这些成员的可见性缺省为private
了解UML图(Unified Modelling Language)统一建模语言描述
简单程序:定义一个电视TV的类,包括频道切换,开关机,音量调节
#include <iostream> using namespace std; class TV { public: int channel; int volume_level; bool on; //定义构造方法 TV() { channel = 1; volume_level = 40; on = false; } void turnOn() { on = true; } void turnOff() { on = false; } void setChannel(int new_channel) { if(on && new_channel >=1 && new_channel<=120) { channel = new_channel; } } void setVolume(int volume) { if(on && volume>=0 && volume<=100) { volume_level = volume; } } void channelUp() { if(on && channel<120) channel++; } void channelDowm() { if(on && channel>0) channel--; } void volumeUp() { if(on && volume_level<100) volume_level++; } void volumeDown() { if(on && volume_level>0) volume_level--; } }; int main(int argc, char *argv[]) { TV tv1; cout << "The state of tv is " << tv1.on << endl; cout << "The channel is " << tv1.channel << endl; cout << "The volume is " << tv1.volume_level << endl; tv1.turnOn(); tv1.setChannel(56); tv1.setVolume(60); cout << "The state of tv is " << tv1.on << endl; cout << "The channel is " << tv1.channel << endl; cout << "The volume is " << tv1.volume_level << endl; tv1.channelUp(); tv1.volumeDown(); cout << "The state of tv is " << tv1.on << endl; cout << "The channel is " << tv1.channel << endl; cout << "The volume is " << tv1.volume_level << endl; return 0; }
2.构造函数
构造函数可以理解为一种特殊的函数,与普通的函数相比,主要不同点有:
1.构造函数的名字必须与类的名字相同
2.构造函数没有返回类型 返回void也不可以
3.创建对象时,构造函数被调用, 他的作用就是初始化对象
构造函数也可以重载(可以同名,但函数签名不同),构造函数用来初始化数据域。
注: 类中的变量作为类的新成员,在声明时不能进行初始化。比如Circle类里面的radius,在声明不能初始化,只能在构造方法中为其赋值。
3.创建及使用对象
对象创建后,可以通过.运算(即对象成员访问运算符)来访问对象的数据以及调用对象的函数
尽量将类名的每个单词首字母大写
“=”可以进行两个对象之间内容的复制,但他们仍然是两个不同的对象
@@,一个对象包含的数据存储在内存里,但是函数并不需要存储,因为函数是这个类所有对象共享的,编译器仅仅创建一份拷贝就可以了。例如对于circle1, circle2,他们的大小都是一个double类型所占的字节的大小。
@@有时创建一个对象,但只需要使用一次,这时无需为对象命名,创建为匿名对象即可
3.类定义和类实现的分离
分离类定义和类实现有利于类的维护。
使用类的程序称为客户程序。
::称为二元作用域解析运算符,指明类成员的作用范围。
分离的好处:
1.可以实现隐藏。在不改变类的定义的前提下,可以自由修改类的实现
2.作为软件供应商,只要提供类定义的文件和类实现的目标代码,从而隐藏了类实现的源代码,保护软件上的知识产产权
类定义放在h文件中,类实现在.cpp文件中
.h文件中类的定义
// 关于类的定义 class Circle { public: // 定义数据域 double radius; // 构建函数 Circle(); // 构建函数 Circle(double); // 定义函数 double getArea(); }
.cpp文件中类的实现
#include "E:\back_up\code\c_plus_code\chapter10\external_file\circle.h" Circle::Circle() { radius = 1.0; } Circle::Circle(double new_radius) { radius = new_radius; } double Circle::getArea() { return 3.14*radius*radius; }
在main.cpp中调用:
#include <iostream> #include "E:\back_up\code\c_plus_code\chapter10\external_file\circle.h" using namespace std; int main(int argc, char *argv[]) { Circle c1; Circle c2(5); cout << "The radius of c1 is " << c1.radius << endl; cout << "The area of c1 is " << c1.getArea() << endl; cout << "The radius of c2 is " << c2.radius << endl; cout << "The area of c2 is " << c2.getArea() << endl; return 0; }
注: include 应该包含circle.h的完整路径,否则会报错
4.避免多次包含
Head.h文件
#include circle.h
Test.cpp文件:
#include circle.h
#include head.h
在上面的代码中,circle.h被重复包含,编译器会报错
如何解决在一个程序中多次包含相同的头文件。如果多次包含,C++编译器会报错,因为预处理器会把#include后的内容直接插入到他们被包含的位置,如果重复包含,就相当于重复插入了相同的内容,就会出现类有多个定义的报错。
包含保护: 原语句 #ifndef和#define可以避免头文件多次被包含,所以在头文件中应该包含。
// 关于类的定义 #ifndef CIRCLE_H #define CIRCLE_H class Circle { public: // 定义数据域 double radius; // 构建函数 Circle(); // 构建函数 Circle(double); // 定义函数 double getArea(); } ; #endif
先测试CIRCLE_H是否已经定义,如果没有,则定义,并且把头文件的其他部分也包含进来,否则跳过、
5. 数据域封装
数据域私有可以保护数据,使类便于维护。
例如,对于类Circle,要防止客户程序直接修改radius的值,以防止客户程序直接修改类的属性,因此,使用关键字private,将数据域设为私有,这就是数据域封装。
一个私有的程序,无法通过直接引用类对象来访问它。 如果需要访问,需改,需要定义一个get()函数和一个set()函数,即访问器(accessor)和更改器(mutator)
Circle类:
circle.h文件
// 关于类的定义 #ifndef CIRCLE_H #define CIRCLE_H class Circle { private: // 数据域封装 double radius; public: // 定义数据域 //double radius; // 构建函数 Circle(); // 构建函数 Circle(double); // 定义函数 double getArea(); // define accessor double getRadius(); // define mutator void setRadius(double); } ; #endif
circle.cpp文件
// 类的实现 #include "E:\back_up\code\c_plus_code\chapter10\external_file\circle.h" Circle::Circle() { radius = 1.0; } Circle::Circle(double new_radius) { radius = new_radius; } double Circle::getArea() { return 3.14*radius*radius; } double Circle::getRadius() // 访问器 { return radius; } void Circle::setRadius(double new_radius) // 更改器 { radius = (new_radius>=0) ? new_radius : 0; //语法糖 }
客户端程序:
#include <iostream> #include "E:\back_up\code\c_plus_code\chapter10\external_file\circle.h" using namespace std; int main(int argc, char *argv[]) { //Circle c1; Circle c2(5); cout << "The radius of c2 is " << c2.getRadius() << endl; cout << "The area of c2 is " << c2.getArea() << endl; c2.setRadius(10.5); cout << "The radius of c2 is " << c2.getRadius() << endl; cout << "The area of c2 is " << c2.getArea() << endl; return 0; }
6.变量作用域
全局变量
局部变量
局部静态变量
在类中,数据域被定义为便变量的形式,可以被所有成员函数访问
在类中,如果一个局部变量和一个数据域有相同的名字,数据域将被覆盖,局部变量的优先级是最高的
7.类的抽象和封装
银行获取贷款的例子:
Loan:
数据域: 利率, 贷款额度, 贷款年限 (用private实现类的封装)
构造方法: Loan()以及Loan(int, double , double)
函数:
year(access, mutator),
loan_amount(access, mutator)
interest_rate(access, mutator)
getMonthlyPayment()
getTotalPayment()
代码:
Loan.h文件:
// loan的定义 #ifndef LOAN_H #define LOAN_H class Loan { public: // 构造方法 Loan(); Loan(int year, double loan_amount, double year_interest_rate); int getYear(); // accessor for year void setYear(int); // mutator for year double getYearInterestRate(); // accessor for interest rate void setYearInterestRate(double); // mutator for interest rate double getLoanAmount(); // access for LoanAmount void setLoanAmount(double); //mutator for double getMonthPayment(); double getTotalPayment(); private: int years; double loan_amounmt; double year_interest_rate; //double month_payment; //double total_payment; }; #endif
Loan.cpp文件:
// 类的实现 #include "E:\back_up\code\c_plus_code\chapter10\external_file\loan.h" #include <cmath> Loan::Loan() { years = 5; loan_amounmt = 100000; year_interest_rate = 4; } Loan::Loan(int year, double amounmt, double rate) // 在类的实现中,参数的名字不能与变量的名字相同 { years = year; loan_amounmt = amounmt; year_interest_rate = rate; } int Loan::getYear() { return years; } void Loan::setYear(int year) { years = year; } double Loan::getYearInterestRate() { return year_interest_rate; } void Loan::setYearInterestRate(double rate) { year_interest_rate = rate; } double Loan::getLoanAmount() { return loan_amounmt; } void Loan::setLoanAmount(double amounmt) { loan_amounmt = amounmt; } double Loan::getMonthPayment() { double month_interest_rate = year_interest_rate / 1200; return loan_amounmt * month_interest_rate / (1 - pow(1 / (1+month_interest_rate), years * 12)); } double Loan::getTotalPayment() { return getMonthPayment() * 12 * years; }
注:在类的实现中函数参数的命名与类中的数据域命名应该不一样,否则会有错误(程序不报错,但会出现错误的结果)
用户程序:
#include <iostream> #include "E:\back_up\code\c_plus_code\chapter10\external_file\circle.h" #include "E:\back_up\code\c_plus_code\chapter10\external_file\loan.h" using namespace std; int main(int argc, char *argv[]) { Loan loan_1; cout << "The year is " << loan_1.getYear() << endl; cout << "loan amount is " << loan_1.getLoanAmount() << endl; cout << "loan rate is " << loan_1.getYearInterestRate() << endl; cout << "month payment is " << loan_1.getMonthPayment() << endl; cout << "total payment is " << loan_1.getTotalPayment() << endl; loan_1.setYear(10); loan_1.setYearInterestRate(8.5); loan_1.setLoanAmount(80000); cout << "-----------------------------" << endl; cout << "The year is " << loan_1.getYear() << endl; cout << "loan amount is " << loan_1.getLoanAmount() << endl; cout << "loan rate is " << loan_1.getYearInterestRate() << endl; cout << "month payment is " << loan_1.getMonthPayment() << endl; cout << "total payment is " << loan_1.getTotalPayment() << endl; Loan loan_2(15, 30000, 6.4); cout << "-----------------------------" << endl; cout << "The year is " << loan_2.getYear() << endl; cout << "loan amount is " << loan_2.getLoanAmount() << endl; cout << "loan rate is " << loan_2.getYearInterestRate() << endl; cout << "month payment is " << loan_2.getMonthPayment() << endl; cout << "total payment is " << loan_2.getTotalPayment() << endl; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)