《C++大学教程》学习笔记(九)
《C++大学教程》学习笔记(九)
1.Time类实例研究
1.1包含防护
在开始之前,先说明一个重要的C++软件工程概念:在头文件中使用“包含防护”,从而避免头文件中的代码被多次包含到同一源代码文件中的情况。
通过在头文件中加入预处理指令#ifndef,#define,#endif即可构成“包含防护”。同时,在预处理指令 #ifndef 和 #define 中,应使用大写的头文件名,并用下划线代替圆点。
具体使用方法如下所示:
//prevent multiple inclusions of header
#ifndef TIME_H
#define TIME_H...
#endif
1.2Time类的实例
创建一个Time类,实现对时间(HH:MM:SS)的基本操作。Time实例中具体实践了“包含防护”,异常处理这些内容,下面给出文件结构及代码:
Time.h文件:
//包含防护
#ifndef Time_h
#define Time_hclass Time{
public:Time();void setTime(int,int,int);void printUniversal() const; //输出格林威治时间void printStandard() const; //输出标准时间private:unsigned int hour;unsigned int minute;unsigned int second;
};#endif /* Time_h */
Time.cpp文件:
#include "Time.h"
#include <iostream>
#include <iomanip>
#include <stdexcept>
using namespace std;Time::Time()
: hour(0),minute(0),second(0)
{//Nothing
}void Time::setTime(int h, int m, int s){if ((h >= 0 && h <=24) && (m >= 0 && m <= 60) && (s >= 0 && s <= 60)) {hour = h;minute = m;second = s;}elsethrow invalid_argument("Wrong Input!\n");}void Time::printUniversal() const {cout << setfill('0') << setw(2) << hour << ":"<< setw(2) << minute << ":"<< setw(2) << second << endl;
}void Time::printStandard() const {cout << setfill('0') << setw(2) << ((hour == 0 || hour == 12) ? 12 : hour % 12) << ":"<< setw(2) << minute << ":"<< setw(2) << second << (hour < 12 ? " AM" : " PM") << endl;
}
main.cpp文件:
#include <iostream>
#include <stdexcept>
#include "Time.h"
using namespace std;int main(){Time t;cout << "The initial universal time is: ";t.printUniversal();cout << "The initial standard time is: ";t.printStandard();t.setTime(13, 27, 6);cout << "\nThe universal time after setTime is: ";t.printUniversal();cout << "The tandard time after setTime is: ";t.printStandard();try {t.setTime(99, 99, -100); } catch (invalid_argument e) {cout << "\nException: " << e.what(); //注意e.what()的what函数是属于logic_error命名空间的}return 0;
}
运行结果如下图所示:
1.3通过对象、引用、指针访问类的public成员
以下三种方式调用成员函数的方式不同,但归根到底都只是对同一个Time对象(即 t )进行操作。
Time t;Time &tRef = t;Time *tPtr = &t;t.setTime(0, 0, 0);tRef.setTime(1, 1, 1);tPtr->setTime(2, 2, 2);
2.改进型Time类:具有默认实参的构造函数
在原有的Time类上加以改进,引入了具有默认实参的构造函数,优化了setTime函数,为每个private数据成员搭配了相应的get、set函数。
具体代码如下所示:
Time.h文件:
//包含防护
#ifndef Time_h
#define Time_hclass Time{
public:explicit Time(int = 0,int = 0,int = 0);void setTime(int,int,int);void setHour(int);void setMinute(int);void setSecond(int);unsigned int getHour() const;unsigned int getMinute() const;unsigned int getSecond() const;void printUniversal() const; //输出格林威治时间void printStandard() const; //输出标准时间private:unsigned int hour; // 0 - 23unsigned int minute; // 0 - 59unsigned int second; // 0 - 59
};#endif /* Time_h */
Time.cpp文件:
#include "Time.h"
#include <iostream>
#include <iomanip>
#include <stdexcept>
using namespace std;Time::Time(int hour,int minute,int second){setTime(hour, minute, second);
}void Time::setTime(int h, int m, int s){setHour(h);setMinute(m);setSecond(s);
}void Time::setHour(int h){if (h >= 0 && h < 24)hour = h;elsethrow invalid_argument("hour must be 0-23");
}void Time::setMinute(int m){if (m >= 0 && m < 60)minute = m;elsethrow invalid_argument("minute must be 0-59");
}void Time::setSecond(int s){if (s >= 0 && s < 60)second = s;elsethrow invalid_argument("second must be 0-59");
}unsigned int Time::getHour() const{return hour;
}unsigned int Time::getMinute() const{return minute;
}unsigned int Time::getSecond() const{return second;
}void Time::printUniversal() const {cout << setfill('0') << setw(2) << hour << ":"<< setw(2) << minute << ":"<< setw(2) << second << endl;
}void Time::printStandard() const {cout << setfill('0') << setw(2) << ((hour == 0 || hour == 12) ? 12 : hour % 12) << ":"<< setw(2) << minute << ":"<< setw(2) << second << (hour < 12 ? " AM" : " PM") << endl;
}
main.cpp文件:
#include <iostream>
#include <stdexcept>
#include "Time.h"
using namespace std;int main(){Time t1;Time t2(2);Time t3(21,34);Time t4(12,25,42);cout << "t1:\n";t1.printUniversal();t1.printStandard();cout << "\nt2:\n";t2.printUniversal();t2.printStandard();cout << "\nt3:\n";t3.printUniversal();t3.printStandard();cout << "\nt4:\n";t4.printUniversal();t4.printStandard();try {Time t5(23,61,59);} catch (invalid_argument e) {cout << "\nException: " << e.what() << endl; //注意e.what()的what函数是属于logic_error命名空间的}return 0;
}
运行结果如下图所示:
3.零零散散
析构函数(destructor)是一种特殊的成员函数,它不接收任何参数,也不返回任何值。当对象撤销时,类的析构函数会隐式地调用。析构函数的声明方式如下所示:
class Employee{public:Employee(const string&,const string&,const Date&,const Date&);~Employee();private:......
};
返回一个private类型的数据成员的引用或指针会破坏类的封装性,可以参考书P299。
一般情况下,类的对象是可以相互赋值的,比如:
Date date1(7,4,2004);
Date date2;date2 = date1;
对于每个类,编译器都提供一个默认的复制构造函数,可以将原始对象(date1)的每个成员复制到新对象(date2)的相应成员中。
但这种复制并不是总是有效的,当所用类的数据成员包含指向动态分配内存的指针时,默认复制构造函数会引发严重问题,这种时候就需要自己去定义一个定制的复制构造函数。
一个“const”对象的“常量性”是从构造函数完成对象的初始化到析构函数被调用之间,因此将构造函数和析构函数声明为const是一个编译错误。
const对象的使用是非常严格的:即const对象只能访问被显式声明为const的成员函数,无论这个函数是否修改了这个对象。
类的friend函数(友元函数)在类的作用域之外定义,却具有访问类的public、private、protected成员的权限。友元定义可以放在类定义的任何地方,即使friend函数的原型在类定义内出现,友元仍不是成员函数。
友元关系是授予的而不是索取的,既不是对称的也不是传递的,声明一个友元函数如下所示:
class Court{friend void setX(Count & , int); //友元函数定义public:......
private:int x;
};
4.组成:对象作为类的成员
软件复用性的一个普遍的形式是组成,即一个类将其他类的对象作为成员。
在下面这个实例中,Employee类中将Date类的两个对象作为成员,具体的文件结构与代码如下所示:
Date.h文件:
#ifndef Date_h
#define Date_hclass Date{public:static const unsigned int monthsPerYear = 12;explicit Date (int = 1,int = 1,int = 1);void print() const;~Date();private:unsigned int month; //1 - 12unsigned int day; //1 - 31 based on monthunsigned int year;unsigned int checkDay(int) const;
};#endif /* Date_h */
Date.cpp文件:
#include "Date.h"
#include <iostream>
#include <array>
#include <stdexcept>
using namespace std;Date::Date(int m,int d,int y){if (m > 0 && m <= monthsPerYear)month = m;elsethrow invalid_argument("month must be 1 - 12\n");year = y;day = checkDay(d);cout << "Date object constructor for date: ";print();cout << endl;}Date::~Date(){cout << "Date object destructor for date: ";print();cout << endl;
}void Date::print() const {cout << month << "/" << day << "/" << year;
}unsigned int Date::checkDay(int testDay) const{static const array< int , monthsPerYear + 1 > daysPerMonth ={0,31,28,31,30,31,30,31,31,30,31,30,31};if (testDay > 0 && testDay <= daysPerMonth[month])return testDay;if (testDay == 29 && month == 2 && (year % 400 == 0 || (year % 4 == 0 && year%100 != 0)))return testDay;throw invalid_argument("Invalid day for current month and year\n");
}
Employee.h文件:
#ifndef Employee_h
#define Employee_h#include "Date.h"
#include <string>
using namespace std;class Employee{public:Employee(const string&,const string&,const Date&,const Date&);~Employee();void print() const;private:string firstName;string lastName;const Date birthDate;const Date hireDate;//当Employee对象撤销时,这两个Date也要撤销,即执行析构函数
};#endif /* Employee_h */
Employee.cpp文件:
#include "Employee.h"
#include "Date.h"
#include <iostream>
using namespace std;Employee::Employee(const string &first,const string &last,const Date &birth,const Date &hire)
:firstName(first),lastName(last),birthDate(birth),hireDate(hire)
{cout << "Employee object constructor: " << firstName << " " << lastName << endl;
}void Employee::print()const {cout << lastName << ", " << firstName << " Birthday: ";birthDate.print();cout << " Hired: ";hireDate.print();cout << endl;
}Employee::~Employee(){cout << "Employee object destructor: " << firstName << " " << lastName << endl;
}
main.cpp文件:
#include "Date.h"
#include "Employee.h"
#include <iostream>
using namespace std;int main(){Date birth(7,24,1949);Date hire(3,12,1988);Employee manager("Bob","blue",birth,hire);cout << endl;manager.print();cout << endl;return 0;}
上述程序的运行结果是:
5.this指针
每个对象都可以用一个称为this的指针来访问自己的地址,通过使用this指针可以避免命名冲突。比如:
//Time类中的setHour成员函数
void Time::setHour(int hour){if (hour >= 0 && hour < 24)this->hour = hour;//等号左边是数据成员hour,等号右边是局部变量hourelsethrow invalid_argument("hour must be 0-23");
}
除了
this->hour
这种访问方式之外,
(* this).hour
也是可以的。
除此之外,使用this指针还能使串联的函数调用成为可能,具体的使用方法可以见书P310。
6.static成员与static成员函数
我们考虑这么一种情况:在Employee类引入一个普通的count变量来记录实际雇员的个数,而Employee类又会产生多个实例对象。当一名雇员加入或离职时,必须对所有对象的count值都进行修改(以保持统一),这显然是不必要的。
事实上,我们只需要一个static的count变量,就能很好的表示这一被所有实例所共享的性质。
由于static成员在对象撤销之后仍然存在,所以要想使用static成员,就必须搭配相应的static成员函数。
我们以Employee类来简单说明:
#ifndef Employee_h
#define Employee_hclass Employee{public:......static unsigned int getCount(); //作为static数据成员搭配使用的static成员函数private:......static unsigned int count; //无法在此初始化,需要到.cpp文件中初始化
};#endif /* Employee_h */
getCount函数的具体实现则是:
unsigned int Employee::getCount(){return count;
}
count的初始化(.cpp文件中)则是:
unsigned int Employee::count = 0;
在main函数中,当没有一个Employee对象时,通过
Employee::getCount()
可以访问到count的值。
而当存在Employ对象时,则通过
Employee e1("a","b");cout << "Employee number after: " << e1.getCount() << endl;
cout << "Employee number after: " << Employee::getCount() << endl << endl;
两种方式均可访问。
《C++大学教程》学习笔记(九)相关推荐
- ROS学习笔记九:用C++编写ROS发布与订阅
ROS学习笔记九:用C++编写ROS发布与订阅 本节主要介绍如何用C++编写一个简单的ROS发布与订阅. 编写发布节点 在之前创建的例子beginner_tutorials软件包中,在其目录下的src ...
- IOS学习笔记(九)之UIAlertView(警告视图)和UIActionSheet(操作表视图)基本概念和使用方法...
IOS学习笔记(九)之UIAlertView(警告视图)和UIActionSheet(操作表视图)基本概念和使用方法 Author:hmjiangqq Email:jiangqqlmj@163.com ...
- python3.4学习笔记(九) Python GUI桌面应用开发工具选择
python3.4学习笔记(九) Python GUI桌面应用开发工具选择 Python GUI开发工具选择 - WEB开发者 http://www.admin10000.com/document/9 ...
- linux磁盘符变化autofs,Linux基础教程学习笔记之Autofs自动挂载
Linux基础教程学习笔记之Autofs自动挂载 Autofs自动挂载: yum -y install autofs vim /etc/auto.master 在文件中添加下面行 /home/gue ...
- 吴恩达《机器学习》学习笔记九——神经网络相关(1)
吴恩达<机器学习>学习笔记九--神经网络相关(1) 一. 非线性假设的问题 二. 神经网络相关知识 1.神经网络的大致历史 2.神经网络的表示 3.前向传播:向量化表示 三. 例子与直觉理 ...
- 网络存储 linux 访问,Linux基础教程学习笔记28——使用Samba访问网络存储
Linux基础教程学习笔记28--使用Samba访问网络存储 SMB用于Windows和类Linux系统直接的文件共享 安装samba client包: [root@linuxidc~]# yum i ...
- unixlinux大学教程学习 第七章
unix&linux大学教程学习 第七章 学习了第七章. 主要讲解键盘信号 每一个键盘对应一个信号.主要的有: ^代表 Ctrl <Backspance> erase ,删除一个字 ...
- at24c16如何划分出多个读写区_AVR学习笔记九、基于AT24C16的数据存储实验
Ema{@AVR 学习笔记九.基于 AT24C16 的数据存储实验 ------- 基于 LT_Mini_M16 9.1 用 I/O 口模拟 I2C 总线实现 AT24C16 的读写 9.1.1 .实 ...
- 无敌python爬虫教程学习笔记(一)
python爬虫系列文章目录 无敌python爬虫教程学习笔记(一) 无敌python爬虫教程学习笔记(二) 无敌python爬虫教程学习笔记(三) 无敌python爬虫教程学习笔记(四) 本文目录 ...
- 无敌python爬虫教程学习笔记(二)
系列文章目录 无敌python爬虫教程学习笔记(一) 无敌python爬虫教程学习笔记(二) 无敌python爬虫教程学习笔记(三) 无敌python爬虫教程学习笔记(四) 手刃一个小爬虫 系列文章目 ...
最新文章
- java实现在pdf文档上填充内容
- react项目部署nginx服务器
- python文件压缩
- JavaScript 技术篇-textContent获取dom节点text文本内容带空格,js获取带空格的dom节点内容
- 关于网上cython书籍的调研
- Contracts for Java
- 软件开发质量的双保险 — 3.应用设计验证与应用用例
- Opencv3.4.2调用yolov2进行物体检测源代码
- 泰山游记:道阻且长,活着走出
- linux打印系统cups原理分析
- 【测试工具】Selenium 自动化浏览器(Python 篇)
- matlab 实现差分求导,matlab循环求导
- log4j 日志书写格式_Log4J日志配置详解
- python和股市一点小知识
- 文件夹或文件的隐藏和加密20201024
- 奔涌吧 后浪!!! 哔哩哔哩 何冰
- php ziparchive 损坏,通过ZipArchive php获取损坏或空拉链
- Python中的字符串驻留机制
- python实现监控URL的一个值小于规定的值--邮件报警
- 记一次apple实体店耳机更换
热门文章
- 2021-03-31
- cdn部署php,Typecho博客全站部署腾讯云CDN教程
- 【无极低码】低代码平台开发日记,低代码平台之sql编程
- python标准库os.path中用来_Python标准库os.path中用来判断指定文件是否存在的方法是_____________。...
- AnyDesk(远程控制软件) 免费版,比Teamviewer好用
- python交换数组中的两个元素_Python 交换数组元素
- 详解Python3中yield生成器的用法
- 【缺陷检测】基于matlab GUI印刷电路板自动缺陷检测【含Matlab源码 1912期】
- 2023届深信服C++A卷笔试
- Android Init Language