《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++大学教程》学习笔记(九)相关推荐

  1. ROS学习笔记九:用C++编写ROS发布与订阅

    ROS学习笔记九:用C++编写ROS发布与订阅 本节主要介绍如何用C++编写一个简单的ROS发布与订阅. 编写发布节点 在之前创建的例子beginner_tutorials软件包中,在其目录下的src ...

  2. IOS学习笔记(九)之UIAlertView(警告视图)和UIActionSheet(操作表视图)基本概念和使用方法...

    IOS学习笔记(九)之UIAlertView(警告视图)和UIActionSheet(操作表视图)基本概念和使用方法 Author:hmjiangqq Email:jiangqqlmj@163.com ...

  3. python3.4学习笔记(九) Python GUI桌面应用开发工具选择

    python3.4学习笔记(九) Python GUI桌面应用开发工具选择 Python GUI开发工具选择 - WEB开发者 http://www.admin10000.com/document/9 ...

  4. linux磁盘符变化autofs,Linux基础教程学习笔记之Autofs自动挂载

    Linux基础教程学习笔记之Autofs自动挂载 Autofs自动挂载: yum -y install autofs vim /etc/auto.master  在文件中添加下面行 /home/gue ...

  5. 吴恩达《机器学习》学习笔记九——神经网络相关(1)

    吴恩达<机器学习>学习笔记九--神经网络相关(1) 一. 非线性假设的问题 二. 神经网络相关知识 1.神经网络的大致历史 2.神经网络的表示 3.前向传播:向量化表示 三. 例子与直觉理 ...

  6. 网络存储 linux 访问,Linux基础教程学习笔记28——使用Samba访问网络存储

    Linux基础教程学习笔记28--使用Samba访问网络存储 SMB用于Windows和类Linux系统直接的文件共享 安装samba client包: [root@linuxidc~]# yum i ...

  7. unixlinux大学教程学习 第七章

    unix&linux大学教程学习 第七章 学习了第七章. 主要讲解键盘信号 每一个键盘对应一个信号.主要的有: ^代表 Ctrl <Backspance> erase ,删除一个字 ...

  8. at24c16如何划分出多个读写区_AVR学习笔记九、基于AT24C16的数据存储实验

    Ema{@AVR 学习笔记九.基于 AT24C16 的数据存储实验 ------- 基于 LT_Mini_M16 9.1 用 I/O 口模拟 I2C 总线实现 AT24C16 的读写 9.1.1 .实 ...

  9. 无敌python爬虫教程学习笔记(一)

    python爬虫系列文章目录 无敌python爬虫教程学习笔记(一) 无敌python爬虫教程学习笔记(二) 无敌python爬虫教程学习笔记(三) 无敌python爬虫教程学习笔记(四) 本文目录 ...

  10. 无敌python爬虫教程学习笔记(二)

    系列文章目录 无敌python爬虫教程学习笔记(一) 无敌python爬虫教程学习笔记(二) 无敌python爬虫教程学习笔记(三) 无敌python爬虫教程学习笔记(四) 手刃一个小爬虫 系列文章目 ...

最新文章

  1. java实现在pdf文档上填充内容
  2. react项目部署nginx服务器
  3. python文件压缩
  4. JavaScript 技术篇-textContent获取dom节点text文本内容带空格,js获取带空格的dom节点内容
  5. 关于网上cython书籍的调研
  6. Contracts for Java
  7. 软件开发质量的双保险 — 3.应用设计验证与应用用例
  8. Opencv3.4.2调用yolov2进行物体检测源代码
  9. 泰山游记:道阻且长,活着走出
  10. linux打印系统cups原理分析
  11. 【测试工具】Selenium 自动化浏览器(Python 篇)
  12. matlab 实现差分求导,matlab循环求导
  13. log4j 日志书写格式_Log4J日志配置详解
  14. python和股市一点小知识
  15. 文件夹或文件的隐藏和加密20201024
  16. 奔涌吧 后浪!!! 哔哩哔哩 何冰
  17. php ziparchive 损坏,通过ZipArchive php获取损坏或空拉链
  18. Python中的字符串驻留机制
  19. python实现监控URL的一个值小于规定的值--邮件报警
  20. 记一次apple实体店耳机更换

热门文章

  1. 2021-03-31
  2. cdn部署php,Typecho博客全站部署腾讯云CDN教程
  3. 【无极低码】低代码平台开发日记,低代码平台之sql编程
  4. python标准库os.path中用来_Python标准库os.path中用来判断指定文件是否存在的方法是_____________。...
  5. AnyDesk(远程控制软件) 免费版,比Teamviewer好用
  6. python交换数组中的两个元素_Python 交换数组元素
  7. 详解Python3中yield生成器的用法
  8. 【缺陷检测】基于matlab GUI印刷电路板自动缺陷检测【含Matlab源码 1912期】
  9. 2023届深信服C++A卷笔试
  10. Android Init Language