C++中,枚举类型、结构类型、类类型是三种用户自定义的符合数据类型。
- 5.1 简单数据类型与构造式数据类型
简单数据类型,又基本数据类型、原子数据类型:该类型的数据是单一的一个值,不可再细分为若干部分。包括int, float, char等;
构造式数据类型:数据内部有多个组成部分,可以分别访问这些组成部分。包括枚举、结构、类。
- 5.2 枚举类型
若想建立一种数据类型,其值域由程序员自定义,可以使用enum定义:
enum <枚举类型名>
{<枚举元素表>};
如:
enum Day {SUN,MON,TUE,WEN,THU,FRI,SAT};
Day day1,day2;
day1=FRI;
day2=day1;
注意:
1 枚举类型使得程序员可自定义数据值域,但该值域的数据值是有限的。
2 使用枚举类型是为了增强代码可读性。
3 从语法角度,枚举元素是命名常量,其实际值为0、1、2、... ...因此,两个枚举类型相同的值可比较,如:
day1=FRI; day2=SAT;
if(day1<day2) cout<<"day1 is before day2";
4 从应用角度,可以将枚举元素视作字面常量。
5 C++规定,枚举类型的值可以直接赋值给整型变量,赋值过程中自动进行类型转换;整型数值不能直接赋值给枚举类型的变量,但可通过强制类型转换后再赋值给枚举类型变量;枚举类型变量不能直接自增或自减,也需要通过强制类型转换后自增。
int val=FRI; // 正确
day1=2; // 错误
day2=Day(2); // 正确
day1++; // 错误
day1=Day(day1+1); // 正确
6 不能通过cin或cout直接输入或输出枚举元素值;只能通过判断,另行赋值或输出。
Day day1=FRI;
switch(day1){
case FRI: cout<<"FRI";return 0;
/*... ...*/
}
int k;cin>>k;
switch(k){
case 0: day1=SUN;break;
case 1: day1=MON;break;
/*... ...*/
}
- 5.3 结构类型
一、结构类型的定义及其变量的声明和使用
1 结构类型的声明(或定义)
struct <结构类型名>
{<定义成员变量的列表>};
2 访问结构变量中的成员可用成员选择符".",形如
结构变量.成员名
举例:
struct StudentRec{ // 声明结构类型
string stuNum; // 定义结构类型的成员变量
string stuName;
int score1;
int score2;
bool admit;
};
StudentRec stu1,stu2; // 用结构类型声明变量stu1和stu2
stu1.stuNum="0001"; // 访问结构变量的成员
stu2.stuNum="0002";
stu1.score1=90;
stu2.admit=false;
注意:StudentRec只是一种数据类型,必须用其声明或定义变量后,该变量才是真正可以被使用的数据。
3 成员变量可以参与在该成员变量类型上可进行的任意操作。
举例:
cin>>stu1.score1>>stu1.score2; // I/O
int mean=(stu1.score1+stu1.score2)/2; // 算术操作
if(mean<60) stu1.admit=false; // 赋值
else stu1.admit=true;
二、结构变量的整体操作
1 结构变量不能作为整体进行I/O、算术、比较操作
2 结构变量可以作为整体赋值给相同类型的另一个变量
3 结构变量可以作为函数参数,形式有值形参、引用形参
在值形参下,实参和形参是两个变量,调用时实参各成员的值传递给形参的相应成员;
在引用形参下,实参和形参是同一个变量。
注意:若需要在被调用函数中修改实参结构变量成员的值,应使用引用形参;否则两者均可使用
若成员变量很多,结构变量所占内存空间很大,因此有时倾向使用引用参数。
举例:
/*
studentRec.h
功能:声明结构类型StudentRec的头文件
*/
#ifndef STUDENTREC_H
#define STUDENTREC_H
#include <string>
using namespace std;
struct StudentRec{
string stuNum;
string stuName;
int score1;
int score2;
bool admit;
};
#endif
/*
client.cpp
功能:使用结构类型StudentRec的客户程序
演示结构类型作函数参数的使用方法
*/
#include "studentRec.h"
#include <iostream>
using namespace std;
void input(StudentRec& student) // 必须使用引用形参
{
cout << "Please enter your name:";
cin >> student.stuName;
cout << "Please enter your number:";
cin >> student.stuNum;
cout << "Please enter your two scores:";
cin >> student.score1 >> student.score2;
cout << "Please enter whether you are admitted.(0 for No, 1 for Yes)";
cin >> student.admit;
}
bool check(StudentRec& student) // 引用形参、值形参均可使用
{
int mean = (student.score1 + student.score2) / 2;
if ((mean < 60 && student.admit == false) || (mean >= 60 && student.admit == true))
return true;
else
return false;
}
int main()
{
StudentRec stu1;
input(stu1);
if (check(stu1))
cout << "Correct input." << endl;
else
cout << "Wrong input." << endl;
system("pause");
return 0;
}
4 结构变量可以作函数返回值
举例:
上述input函数可以改写成带有返回值的函数
StudentRec input()
{
StudentRec student;
cin >> student.stuNum >> student.stuName;
cin >> student.score1 >> student.score2;
cin >> student.admit;
return student;
}
input函数调用形式:
StudentRec stu1 = input();
三、层次结构
层次结构变量:结构变量的成员也是结构变量。
四、匿名结构类型
结构类型也可以是匿名结构,使用匿名结构类型定义结构变量的一般形式如下:
struct{
<定义成员变量的列表>
}<结构变量名>;
一般情况下,倾向先定义有名字的结构类型,再使用这种类型定义变量。
- 5.4 类与对象
一、类
类与结构相似,但通常类中的成员既有数据,也有作用在该数据的操作。
class Date{
public:
void set(int newMonth, int newDay, int newYear);
int getMonth() const;
int getDay() const;
int getYear() const;
private:
int month;
int day;
int yaer;
};
1 类的成员有数据成员、函数成员
一般,数据成员需要隐藏,即外部程序不能直接访问该数据,应通过函数成员访问;
通过关键字private声明私有成员,通过关键字public声明公有成员;
2 类中声明的数据成员不能在声明中进行初始化,必须通过构造函数进行初始化;
3 类的数据成员在声明时不能使用关键字auto、register、extern进行修饰,但可用保留字static修饰
声明时使用static修饰的数据成员称为静态数据成员;
静态数据成员存放在公共内存区,由该类的所有实例共享,相当于类中的全局变量,可起到在同类对象间共享信息的作用;
静态数据成员必须在类定义体的外部定义一次(且只定义一次),在定义时进行初始化。
class C{
static int sdm; // 在类C中声明静态数据成员sdm
// 其他成员忽略
};
int C::sdm = 3; // 定义并初始化静态数据成员
// 注意:此时不再使用保留字static
4 声明类的成员函数时也可使用保留字static进行修饰,称为静态成员函数
静态成员函数没有隐含的this指针,可以直接访问该类中de静态数据成员,但不可直接使用该类的非静态数据成员;
静态成员函数也不能声明为虚函数;
静态成员函数也不能指定为常量成员函数;
5 成员函数声明中圆括号后带有关键字const,表示在该函数的实现中,不能修改该类中的数据成员的值,只能读取,否则出现语法错误
带const的成员函数称为常量成员函数;
如果想修改某个成员变量,可在其成员变量声明语句前面加上mutable修饰符,则该成员变量永远可变,即使在一个const函数中
6 类的共哟ujingtaichengyuan可通过类名及作用域分辨操作符"::"访问
二、对象的创建
1 创建对象
用类声明的变量称为对象;
由于同一个类的不同对象的数据成员不同,因此各对象的数据成员在内存中占据不同的内存空间(静态数据成员除外,其存放在类的公用区);
但同一类对象的成员函数的代码都是相同的,因此成员函数没必要有多个备份,而只需一个即可,节省内存空间。
2 使用对象的成员
对象创建后,可通过成员选择符"."访问其public成员。
三、对象的初始化
一个对象的数据成员的取值反映了该对象的当前状态。用户代码通常通过调用对象提供的公有函数设置该对象数据成员的值。
但在创建对象后,若忘记调用该函数设置正确的值就进行操作,很可能发生错误。
我们希望,在创建一个对象的同时,某个函数就会自动被调用,而我们只需在该函数中写入一些初始化数据成员的代码,即可保证对象创建完毕后具有确定的值,该函数称为构造函数。
1 构造函数
构造函数也是类的成员函数;
构造函数名必须与类型相同;
构造函数没有返回值,也没有返回类型,即使是void类型也不允许;
一个类中可以有多个构造函数,但由于各个构造函数的名称一样,因此需要不同的参数类型或参数个数列表加以区分;
没有参数的构造函数称为默认构造函数;
在创建对象时,调用哪个构造函数取决于实参列表;
构造函数是在创建对象时自动调用的,而不是通过"."显式调用的;
若设计的类中没有构造函数,C++编译器会自动为该类建立一个默认构造函数,该构造函数没有任何参数,且函数体为空。
举例:
#include <iostream>
using namespace std;
class Date {
public:
Date(int initYear, int initMonth, int initDay); // 构造函数
Date(); // 默认构造函数
// 其他成员函数
private:
int month;
int day;
int year;
};
Date::Date(int initYear, int initMonth, int initDay)
{
int year = initYear; // 在构造函数中进行初始化
int month = initMonth;
int day = initDay;
}
Date::Date()
{
year = 2015;
month = 3;
day = 29;
}
int main()
{
Date date1; // 自动调用默认构造函数
Date date2(1988, 3, 25); // 自动调用里ing一个带有参数的构造函数
// 其他代码实现
}
2 析构函数
析构函数作用是在撤销对象时执行一些清理任务,如在构造函数中动态分配的内存空间通常在析构函数中释放等;
C++语言规定析构函数名是类名前加波浪号“~”,以区别构造函数;
析构函数不能有任何返回类型,且不能带有任何参数;
在用户代码中,可通过"."显式调用析构函数;但在更多情况下,是在对象生存期结束时由系统自动调用析构函数;
析构函数最典型的用法是在撤销对象时回收内存空间;
举例:
#include <iostream>
using namespace std;
class Array{
public:
Array(int initLength); // 声明构造函数
~Array(); // 声明析构函数
private:
int length;
float* p;
};
Array::Array(int initLength) // 定义构造函数
{
length = initLength;
p = new float[length];
}
Array::~Array() // 定义析构函数
{
delete[] p;
}
int main()
{
Array arr(100); // 定义Array类对象时,自动调用构造函数对成员变量进行初始化
return 0; // main函数结束撤销arr变量时,自动调用析构函数
}
- 5.5 应用举例
设计一个类Account,表示客户的活期账户。
Account类中有账号AccountNum、户名name、当前存款余额balance等属性;
Account类中有存款deposite、取款withdraw、查询余额getBalance、查询账号getNumber、查询户名getName等成员函数。
编写一个客户程序,用以测试Account的设计是否正确。
/*
account.h
类Account的说明文件
*/
#ifndef ACCOUNT_H
#define ACCOUNT_H
#include <string>
using namespace std;
class Account{
public:
Account(string initAccountNum, string initName, double initBalance);
void deposite(double depositeMoney);
bool withdraw(double withdrawMoney);
double getBalance() const;
string getNumber() const;
string getName() const;
void print() const;
private:
string number;
string name;
double balance;
};
#endif
/*
account.cpp
类Account的实现文件
*/
#include "account.h"
#include <iostream>
Account::Account(string initAccountNum, string initName, double initBalance)
{
number = initAccountNum;
name = initName;
balance = initBalance;
}
void Account::deposite(double depositeMoney)
{
balance += depositeMoney;
}
bool Account::withdraw(double withdrawMoney)
{
if (balance >= withdrawMoney)
{
balance -= withdrawMoney;
return true;
}
else
return false;
}
double Account::getBalance() const
{
return balance;
}
string Account::getNumber() const
{
return number;
}
string Account::getName() const
{
return name;
}
void Account::print() const
{
cout << "Number: " << number << endl
<< "Name: " << name << endl
<< "Balance: " << balance << endl;
}
#include "account.h"
#include <iostream>
using namespace std;
int main()
{
Account act1("0100", "ZhangSan", 200);
Account act2("0101", "LiSi", 101);
cout << "Account 1:"<<endl;
act1.print();
cout << "Account 2:"<<endl;
act2.print();
act1.deposite(200);
if (!act2.withdraw(150))
{
cout << "Overdraft is not permitted." << endl;
}
cout << "Account 1:"<<endl;
act1.print();
cout << "Account 2:" << endl;
act2.print();
system("pause");
return 0;
}