目录
五、类的组合
类中的成员是另一个类的对象
1.类的组合的构造函数设计
·原则:对本类中的基本类型成员数据以及对象成员均要初始化
·语法形式:
类名::类名(对象成员所需的形参,本类成员形参) : 对象1(参数),对象2(参数),…… { //函数体其他语句 }
2.初始化次序
·对构造函数初始化列表中列出的成员(包括基本类型成员和对象成员)进行初始化,初始化次序是成员在类体中定义的次序
a.成员对象构造函数调用顺序:按对象成员的声明顺序,先声明者先构造
b.初始化列表中未出现的成员对象,调用默认构造函数(即无形参的)初始化
·处理完初始化列表之后,执行构造函数的函数体
举例:
#include<iostream>
#include<cmath>
using namespace std;
class Point { //Point类定义
public:
Point(int xx = 0, int yy = 0) { x = xx, y = yy; }
Point(Point &p);
int getX() { return x; }
int getY() { return y; }
private:
int x, y;
};
Point::Point(Point &p) { //复制构造函数的实现
x = p.x;
y = p.y;
cout << "Calling the copy constructor of Point" << endl;
}
//类的组合
class Line { //Line类的定义
public: //外部接口
Line(Point xp1, Point xp2);
Line(Line &l);
double getLen() { return len; }
private: //私有数据成员
Point p1, p2; //Point类的对象p1,p2
double len;
};
//组合类的构造函数
Line::Line(Point xp1, Point xp2) : p1(xp1), p2(xp2) {
cout << "Calling constructor of Line" << endl;
double x = static_cast<double>(p1.getX() - p2.getX());
double y = static_cast<double>(p1.getY() - p2.getY());
len = sqrt(x * x + y * y);
}
Line::Line(Line &l) : p1(l.p1), p2(l.p2) {//组合类的复制构造函数
cout << "Calling the copy constructor of Line" << endl;
len = l.len;
}
//主函数
int main() {
Point myp1(1, 1), myp2(4, 5); //建立Point类的对象
Line line(myp1, myp2); //建立Line类的对象
Line line2(line); //利用复制构造函数建立一个新对象
cout << "The length of the line is: ";
cout << line.getLen() << endl;
cout << "The length of the line2 is: ";
cout << line2.getLen() << endl;
return 0;
}
3.前向引用声明
·类应该先声明,后使用
·如果需要在某个类的声明之前,引用该类,则应进行前向引用声明
·前向引用声明只为程序引入一个标识符,但具体声明在其他地方
class B; //前向引用声明
class A {
public:
void f(B b);
};
class B {
public:
void g(A a);
};
注:
·使用前向引用声明虽然可以解决一些问题,但它并不是万能的
·在提供一个完整的类声明之前,不能声明该类的对象,也不能在内联成员函数中使用该类的对象
·当使用前向引用声明时,只能使用被声明的符号,而不能涉及类的任何细节
class Fred; //前向引用声明
class Barney {
Fred x; //错误:类Fred的声明尚不完善
};
class Fred {
Barney y; //正确
};
·在给出完整的类的定义前,可以声明类的引用或者对象的指针
class A;
class B {
A&a; //正确,前向应用声明了类A,可以使用类对象的引用
A*c; //正确,前向应用声明了类A,可以使用类对象的指针
};
六、结构体与联合体
结构体
1.结构体是一种特殊形态的类
·与类的唯一区别:类的缺省访问权限是private,结构体的缺省访问权限是public
·结构体存在的主要原因:与C语言保持兼容
2.用结构体而不用类的情况
·定义主要用来保存数据而没有什么操作的类型
·习惯将结构体的数据成员设为公有,此时用结构体更方便
3.语法形式
struct 结构体名称 {
公有成员
protected:
保护型成员
private:
私有成员
};
4.初始化
若
·一个结构体的全部数据成员都是公有成员
·没有用户定义的构造函数
·没有基类和虚函数
则
结构体的变量可以用以下语法形式初始化: 类型名 变量名 = { 成员数据1初值,成员数据2初值,…… };
举例:
#include<iostream>
#include<string>
#include<iomanip>
using namespace std;
struct Student { //学生信息结构体
int num; //学号
string name; //姓名,字符串对象,将在第6章详细介绍
char sex; //性别
int age; //年龄
};
int main() {
Student stu = { 97001, "Lin Lin", 'F', 19 };
cout << "Num:" << stu.num << endl;
cout << "Name:" << stu.name << endl;
cout << "Sex:" << stu.sex << endl;
cout << "Age:" << stu.age << endl;
return 0;
}
联合体
1.说明
·成员共用同一组内存单元
·任何两个成员不会同时有效
2.语法形式
union 联合体名称 {
公有成员
protected:
保护型成员
private:
私有成员
};
举例:
#include<iostream>
#include<string>
using namespace std;
class ExamInfo {
private:
string name; //课程名称
enum { GRADE, PASS, PERCENTAGE } mode;//计分方式
union {
char grade; //等级制的成绩
bool pass; //只记是否通过课程的成绩
int percent; //百分制的成绩
};
public:
//三种构造函数,分别用等级、是否通过和百分初始化
ExamInfo(string name, char grade)
: name(name), mode(GRADE), grade(grade) { }
ExamInfo(string name, bool pass)
: name(name), mode(PASS), pass(pass) { }
ExamInfo(string name, int percent)
: name(name), mode(PERCENTAGE), percent(percent) { }
void show();
};
void ExamInfo::show() {
cout << name << ": ";
switch (mode) {
case GRADE: cout << grade; break;
case PASS: cout << (pass ? "PASS" : "FAIL"); break;
case PERCENTAGE: cout << percent; break;
}
cout << endl;
}
int main() {
ExamInfo course1("English", 'B');
ExamInfo course2("Calculus", true);
ExamInfo course3("C++ Programming", 85);
course1.show();
course2.show();
course3.show();
return 0;
}
七、枚举类
枚举类型
//<enum> <枚举类型名> {变量值列表}
enum Weekday {SUN, MON, TUE, WED, THU, FRI, SAT}
//默认情况下, SUN = 0, MON = 1, TUE = 2, ……, SAT = 6
说明
·枚举元素是常量,不能对其赋值,即不能写赋值表达式:SUN = 0
·枚举元素有默认值,依次为:0,1,2,…
·可以在声明时另行指定枚举元素的值
enum Weekday {SUN = 7,MON = 1, TUE, WED, THU, FRI, SAT} //TUE往后依次为2,3,……
·枚举值可以进行关系运算
·整数值不能直接赋给枚举变量;如若需要,应进行强制类型转换 (当然,整数应在枚举类型的取值范围之内)
·枚举值可以赋给整型变量
举例:
#include<iostream>
using namespace std;
enum GameResult { WIN, LOSE, TIE, CANCEL };
int main() {
GameResult result;
// enum GameResult omit = CANCEL; 注意赋值格式!!!后面即可用omit代替CANCEL
for (int count = WIN; count <= CANCEL; count++) {
result = GameResult(count);
switch (result) {
case WIN: cout << "The game was played and we won!" << endl; break;
case LOSE: cout << "The game was played and we lost!" << endl; break;
case TIE: cout << "The game was played." << endl; break;
default: cout << "The game was cancelled." << endl;
}
}
return 0;
}
枚举类
1.语法形式
//enum class 枚举类型名: 底层类型 {枚举值列表};
enum class Type { General, Light, Medium, Heavy};
enum class Type: char { General, Light, Medium, Heavy};
enum class Category { General=1, Pistol, MachineGun, Cannon};
2.优势
·强作用域,其作用域限制在枚举类中
Type::General //使用Type的枚举值General
·转换限制,枚举类对象不可以与整型隐式地互相转换
·可以指定底层类型
enum class Type: char { General, Light, Medium, Heavy};
举例:
//错误版
#include<iostream>
using namespace std;
enum class Side { Right, Left };
enum class Thing { Wrong, Right }; //Right不冲突
int main() {
Side s = Side::Right;
Thing w = Thing::Wrong;
cout << (s == w) << endl; //编译错误,无法直接比较不同枚举类
return 0;
}
//正确版
#include<iostream>
using namespace std;
enum class Enumeration1 {
Val1, // 0
Val2, // 1
Val3 = 100,
Val4 /* = 101 */
};
// 指定类型
enum class Enumeration2 : long { val1, val2 = 100, val3 }; // val2=100.000400 出错
int main() {
Enumeration1 my = Enumeration1::Val3;
cout << static_cast<int>(my) << endl;
cout << static_cast<double>(Enumeration2::val2) << endl;
return 0;
}
八、UML简介
UML是一种统一建模语言,是以标准方式表示面向对象系统的图形语言;在UML中,用UML类图建模每个类
·类图是一个具有三个组成部分的矩形。最上面部分居中并加粗显示类名;中间部分是类的属性,对应于C++中的数据成员;底部是类的行为,对应于C++中的成员函数
·通过列出行为名和其后的括号建模行为
·通过在行为名后的括号中列出形参名和随后的冒号以及该形参的类型来建模形参
·通过列出属性名及其后的冒号和属性类型,将数据成员表示为属性
·通过在行为名后的括号后面放置一个冒号以及该返回值类型表示行为值的返回值类型
·通过把双尖括号括起来的constructor放在构造函数名前面区分构造函数与类,且习惯上把类的构造函数放在其他行为前面
·行为名前面的加号(+)表示某某函数是UML中的public行为(即C++中的public成员函数)
·属性名前面的减号(-)表示某某属性是UML中的private属性(即C++中的private数据成员)
九、一些细节
·创建一个类的对象之前,必须告诉编译器这个类有哪些数据成员和成员函数
·自定义的类名通常首字母大写,建议类名中每个单词也首字母大写
·类的每个对象在内存中维护自己的属性复制
·默认情况下,string的初始值是空字符串
·构造函数通常声明为public类型
·编译器只创建一份类的成员函数副本,并在类的所有对象间共享这份副本
·类的接口由类的public成员函数构成;将接口与实现分离即将成员函数放在类外定义