栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

第4章 类与对象

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

第4章 类与对象

第4章 类与对象 类和对象 类和对象的定义

对象是现实中的对象在程序中的模拟

类是同一类对象的抽象

对象是类的实例

定义类的对象,才可以通过对象使用类中定义的功能。

类定义的语法形式
class 类名称
{
    public:
    	公有成员(外部接口)
    private:
    	私有成员
    protected:
    	保护性成员
};

为数据成员设置类内初始值用于初始化数据成员 类内初始值举例

类成员的访问控制

公有类型成员私有类型成员保护类型成员 公有类型成员

在关键字public后面声明,它们是类与外部的接口,任何外部函数都可以访问公有类型数据和函数。 私有类型成员

在关键字private后面声明,只允许本类中的函数访问,而类外部的任何函数都不能访问。如果紧跟在类名称的后面声明私有成员,则关键字private可以省略。 保护类型成员

与private类似,其差别表现在继承与派生时对派生类的影响不同,详见第七章。

对象定义的语法

类名 对象名;

例:

Clock myClock;

类中成员之间直接使用成员名互相访问

从类外访问成员使用“对象名.成员名”方式访问public成员

类的成员函数

在类中声明函数原型;可以在类外给出函数体实现,并在函数名前使用类名加以限定;也可以直接在类中给出函数体,形成内联成员函数;允许声明重载函数和带默认参数值的函数。 内联成员函数

为了提高运行时的效率,对于较简单的函数可以声明为内联形式。内联函数体中不要有复杂结构(如循环语句和switch语句)。在类中声明内联函数的方式:

将函数体放在类的声明中使用inline关键字 类和对象的程序举例

#include
using namespace std;
class Clock{
public:
    void setTime(int newH = 0, int newM = 0, int newS = 0);
    void showTime();
private:
    int hour, minute, second;
};
void Clock::setTime(int newH, int newM, int newS){
    hour = newH;
    minute = newM;
    second = newS;
}
void Clock::showTime(){
    cout << hour << ":" << minute << ":" << second;
}
int main(){
    Clock myClock;
    myClock.setTime(8, 30 ,30);
    myClock.showTime();
    return 0;
}
8:30:30
构造函数 构造函数基本概念 构造函数

类中的特殊函数用于描述初始化算法 构造函数的作用

在对象被创建时使用特定的值构造对象,将对象初始化为一个特定的初始状态。例如:

希望构造一个Clock类对象时,将初始时间设为0:0:0,就可以通过构造函数来设置。 构造函数的形式

函数名与类名相同不能定义返回值类型,也不能有return语句可以有形式参数,也可以没有形式参数可以是内联函数可以重载可以带默认参数值 构造函数的调用时机

在对象创建时被自动调用

例如:

Clock myClock(0, 0, 0)

默认构造函数

调用时可以不需要实参的构造函数

参数表为空的构造函数全部参数都有默认值的构造函数

下面两个都是默认构造函数,如在类中同时出现,将产生编译错误;

Clock();
Clock(int newH = 0, int newM = 0, int newS = 0);
隐含生成的构造函数

如果程序中未定义构造函数,编译器将自动生成一个默认构造函数

参数列表为空,不为数据成员设置初始值;如果类内定义了成员的初始值,则以默认方式初始化;基本类型的数据默认初始化的值是不确定的。 “=default”

如果程序中已定义构造函数,默认情况下编译器就不再隐含生成默认构造函数。如果此时依然希望编译器隐含生成默认构造函数,可以使用“=default”。

例:

class Clock{
public:
    Clock() = default;//指示编译器提供默认构造函数
    Clock(int newH, int newM, int newS);//构造函数
private:
    int hour, minute, second;
};
构造函数例题(1)——例4-1
class Clock{
public:
    setTime(int newH, int newM, int newS);//构造函数
    void showTime();
private:
    int hour, minute, second;
};
//构造函数的实现:
Clock::Clock(int newH, int newM, int newS){
    hour(newH), minute(newM), second(newS){
}
//其他函数实现同4_1
int main(){
    Clock c(0, 0, 0);//自动调用构造函数
    c.showtime();
    return 0;
}
构造函数例题(2)——例4-2
class Clock{
public:
    Clock(int newH, int newM, int newS);//构造函数
    Clock();//默认构造函数
    setTime(int newH, int newM, int newS);
    void showTime();
private:
    int hour, minute, second;
};
Clock::Clock():hour(0), minute(0), second(0){
}
//默认构造函数
//其他函数实现同前
int main(){
    Clock c1(0, 0, 0);//调用有参数的构造函数
    Clock c2;//调用无参数的构造函数
    ......
}
委托构造函数

委托构造函数使用类的其他构造函数执行初始化过程

例如:

Clock(int newH, int newM, int newS):
hour(newH), MINUE(NewM), second(newS){
    
}
Clock():Clock(0, 0, 0){}
复制构造函数 复制构造函数定义

复制构造函数是一种特殊的构造函数,其形参为本类的对象引用。作用是用一个已存在的对象去初始化同类型的新对象。

class 类名{
public:    
    类名(形参);//构造函数
    类名(const 类名 &对象名);//复制构造函数
    //...
};
类名::类(const 类名 &对象名)//复制构造函数的实现
{函数体}
复制构造函数被调用的三种情况

定义一个对象时,以本类另一个对象作为初始值,发生复制构造;如果函数的形参是类的对象,调用函数时,将使用实参对象初始化形参对象,发生复制构造;如果函数的返回值是类的对象,函数执行完成返回主调函数时,将使用return语句中的对象初始化一个临时无名对象,传递给主调函数,此时发生复制构造。

这种情况也可以通过移动构造避免不必要的复制(第6章介绍) 隐含的复制构造函数

如果程序员没有为类声明拷贝初始化构造函数,则编译器自己生成一个隐含的复制构造函数。这个构造函数执行的功能是:用初始化对象的每个数据成员,初始化将要建立的对象的对应数据成员。 “=delete”

如果不希望对象被复制构造

C++98做法:将复制构造函数声明为private,并且不提供函数的实现。

C++11做法:用“=delete”指示编译器不生成默认复制构造函数。

例:

class Point{//Point类的定义
public:
    Point(int xx = 0, int yy = 0){x = xx; y = yy;}//构造函数,内联
    Point(const Point& p) = delete;//指示编译器不生成默认复制构造函数
private:
    int x, y;//私有数据
};
复制构造函数调用举例
#include 
using namespace std;
class 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(const Point &p){
    x = p.x;
    y = p.y;
    cout << "Calling the copy constructor" << endl;
}
void fun1(Point p){
    cout << p.getX() << endl;
}
Point fun2(){
    Point a;
    return a;
}
int main(){
    Point a;
    Point b(a);
    cout << b.getX() << endl;
    fun1(b);
    b = fun2();
    cout << b.getX() << endl;
    return 0;
}
析构函数

析构函数完成对象被删除前的一些清理工作

如果程序中未声明析构函数,编译器将自动产生一个默认的析构函数,其函数体为空。

析构函数的原型 ~类名();析构函数没有参数,没有返回类型

#include 
using namespace std;
class Point{
public:
    Point(int xx, int yy);
    ~Point();
    //...原型
private:
    int x, y;
};
Point::Point(int xx, int yy){
    x = xx;
    y = yy;
}
Point::~Ponit{
    
}
//...其他函数的实现略
类的组合 类的组合

组合的概念

类中的成员是另一个类的对象。可以在已有抽象的基础上实现更复杂的抽象。 类组合的构造函数设计

原则:不仅要对本类的基本类型成员数据初始化,也要对对象成员初始化。

声明形式:

类名::类名(对象成员所需的形参, 本类成员形参):
	对象1(参数), 对象2(参数),......
{
	//函数体其他语句
}
构造组合类对象时的初始化次序

首先对构造函数初始化列表中列出的成员(包括基本类型成员和对象成员)进行初始化,初始化次序是成员在类体中定义的次序。

成员对象构造函数调用顺序:按对象成员的定义顺序,先声明先构造。初始化列表中未出现的成员对象,调用默认构造函数(即无形参的)初始化 处理完初始化列表之后,再执行构造函数的函数体。 类的组合程序举例

#include 
#include 
using namespace std;
class 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{
public:
    Line(Point xp1, Point xp2);
    Line(Line &l);
    double getLen(){
        return len;
    }
private:
    Point p1, p2;
    double len;
};
Line::Line(Point xp1, Point xp2){
    cout << "Calling the copy constructor of Line" << endl;
    double x = static_cast(p1.getX() - p2.getX());
    double y = static_cast(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);
    Line line(myp1, myp2);
    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;
}
前向引用声明

类先声明,后使用如果需要在某个类的声明之前,引用该类,则应进行前向引用声明。前向引用声明只为程序引入标识符,但具体声明在其他地方。

例:

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;
};
UML简介 UML有三个基本的部分

事物(Things)关系(Relationships)图(Diagrams) 类图

举例

Clock类的完整表示

Clock类的简洁表示

对象图

举例

Clock类对象的完整表示

Clock类对象的简洁表示

依赖关系

作用关系——关联

包含关系——聚集

共享聚集:部分可以参加多个整体。组成聚集(组合):整体拥有各个部分,整体与部分共存,如果整体不存在了,部分也就不存在了。

例4-5 用UML描述例4-4中Line类和Point类的关系。

继承关系——泛化

注释

例4-6 带有注释的Line类和Point类的关系

结构体与联合体 结构体(例4-7)

结构体是一种特殊形态的类。与类的唯一区别:

类的缺省访问权限是private,结构体的缺省访问权限public。 什么时候用结构体而不用类

定义主要用来保存数据、而没有什么操作的类型。人们习惯将结构体的数据成员设为公有,因此这时用结构体方便。 结构体定义

struct 结构体名称{
    公有成员
protected:
    保护性成员
private:
    私有成员
};

结构体中可以有数据成员和函数成员

结构体的初始化

如果:

一个结构体的全部数据成员都是公共成员;没有用户定义的构造函数;没有基类和虚函数(基类和虚函数第7章介绍)

这个结构体的变量可以用下面的语法形式初始化:

类型名 变量名 = {成员数据1初值, 成员数据2初值, …};

#include 
#include 
#include 
using namespace std;
struct Student{
    int num;
    string name;
    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;
}
Num: 97001
Name: Lin Lin,
Sex: F
Age: 19
联合体(例4-8) 定义形式
union 结构体名称{
    公有成员
protected:
    保护性成员
private:
    私有成员
};
特点

成员共用同一组内存单元任何两个成员不会同时有效 联合体的内存分配

举例说明:

union Mark{
    char grade;
    bool pass;
    int percent;
}
无名联合

例:

union{
    int i;
    float f;
}

在这个程序中可以这样使用

i = 10;
f = 2.2;

例4-8使用联合体保存成绩信息,并且输出。

#include 
#include 
using namespace std;
class ExamInfo{
private:
    string name;
    enum{GRADE, PASS, PERCENTACE} 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(PERCENTACE), percent(percent){}
    void show();
}
void ExamInfo::show(){
    cout << name << ": ";
    switch(mode){
        case GRADE: cout << grade;break;
        case PASS: cout << (pass ? "PASS" : "FAIL");break;
        case PERCENTACE: 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;
}
English: B
Calculus: PASS
C++ Programming: 85
枚举类 枚举类定义

语法形式

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};
枚举类的优势

强作用域,其作用值限制在枚举类中。

例:使用Type的枚举值General:

​ Type::General

转换限制,枚举类对象不可以与整型隐式的互相转换。

可以指定底层类型

例:

enum classType:char {General, Light, Medium, Heavy};
#include 
using namespace std;
enum class Side{Right, Left};
enum class Thing{Wrong, Right};//不冲突
int main(){
    Side s = Side::Right;
    Thing w = Thing::Wrong;
    cout << (s == w) << endl;//编译错误,无法直接比较不同枚举类
    return 0;
}
实验四(上)

例题一

声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,有两个公有成员函数run、stop。

其中:

rank为枚举类型CPU_Rank,声明为enum CPU_Rank {P1 = 1, P2, P3, P4, P5, P6, P7},frequency为单位是MHz的整型数,voltage为浮点型的电压值。

注意不同访问属性的成员访问方式,并观察构造函数和析构函数的调用顺序。

#include 
using namespace std;
enum CPU_Rank {P1 = 1, P2, P3, P4, P5, P6, P7};
class CPU{
private:
    CPU_Rank rank;
    int frequency;
    float voltage;
public:
    CPU(CPU_Rank r, int f, float v){
        rank = r;
        frequency = f;
        voltage = v;
        cout << "构造了一个CPU!" << endl;
    }
    ~CPU(){cout << "析构了一个CPU!" << endl;}
    CPU_Rank GetRank() const {return rank;}
    int GetFrequency() const {return frequency;}
    float GetVoltage() const {return voltage;}
    void SetRank(CPU_Rank r){rank = r;}
    void SetFrequency(int f){frequency = f;}
    void SetVoltage(float v){voltage = v;}
    void Run(){cout << "CPU开始运行!" << endl;}
    void Stop(){cout << "CPU停止运行!" << endl;}
};
int main(){
    CPU a(P6, 300, 2.8);
    a.Run();
    a.Stop();
    return 0;
}

扩展练习:

类似的,声明RAM类、CDROM类。为实验四下半部分基于类的组合来构建Computer类打下一个基础。

提示:

    RAM类的主要参数包括:容量、类型和主频;类型建议用枚举类型(DDR4/DDR3/DDR2…)。CD-ROM类的主要参数包括:接口类型、缓存容量、安装方式;接口类型建议用枚举类型(SATA、USB…),安装方式建议用枚举类型(external/built-in)。
实验四(下)

例题一

在实验四(上)中声明的CPU类、RAM类、CDROM类基础之上,再声明Computer类。

要求:

    其中声明私有数据成员cpu、ram、cdrom,声明公有成员函数run、stop,可在其中输出提示信息。在main()函数中声明一个Computer类的对象,调用其函数成员。
#include 
using namespace std;
enum CPU_Rank {P1 = 1, P2, P3, P4, P5, P6, P7};
class CPU{
private:
    CPU_Rank rank;
    int frequency;
    float voltage;
public:
    CPU(CPU_Rank r, int f, float v){
        rank = r;
        frequency = f;
        voltage = v;
        cout << "构造了一个CPU!" << endl;
    }
    ~CPU(){cout << "析构了一个CPU!" << endl;}
    CPU_Rank GetRank() const {return rank;}
    int GetFrequency() const {return frequency;}
    float GetVoltage() const {return voltage;}
    void SetRank(CPU_Rank r){rank = r;}
    void SetFrequency(int f){frequency = f;}
    void SetVoltage(float v){voltage = v;}
    void Run(){cout << "CPU开始运行!" << endl;}
    void Stop(){cout << "CPU停止运行!" << endl;}
};
enum RAM_Type{DDR2 = 2, DDR3, DDR4};
class RAM{
private:
    enum RAM_Type type;
    unsigned int frequency;
    unsigned int size//GB
public:
    RAM(RAM_Type t, unsigned int f, unsigned int s){
        type = t;
        frequency = f;
        size = s;
        cout << "构造了一个RAM!" << endl;
    }
    ~RAM(){cout << "析构了一个RAM!" << endl;}
    RAM_Type GetType() const {return type;}
    unsigned int GetFrequency() const {return frequency;}
    unsigned int GetSize() const {return size;}
    void Setype(RAM_Type t){type = t;}
    void SetFrequency(unsigned int f){frequency = f;}
    void SetSize(unsigned int s){size = s;}
	void Run(){cout << "RAM开始运行!" << endl;}
    void Stop(){cout << "RAM停止运行!" << endl;}
};
enum CDROM_Interface{SATA, USB};
enum CDROM_Install_type{external, built_in};
class CD_ROM{
private:
    enum CDROM_Interface interface_type;
    unsigned int cache_size;//MB 
    CDROM_Install_type install_type;
public:
    CD_ROM(CDROM_Interface i, unsigned int s, CDROM_Install_type it){
        interface_type = i;
        cache_size = s;
        install_type = it;
        cout << "构造了一个CD_ROM!" << endl;
    }
    ~CD_ROM(){cout << "析构了一个CD_ROM!" << endl;}
    CDROM_Interface GetInterfaceType() const {return interface_type;}
    unsigned int GetSize() const {return cache_size;}
    CDROM_Install_type GetInstall_type() const {return install_type;}
    void SetInterfaceType(DROM_Interface i){interface_type = i;}
    void SetSize(unsigned int s){cache_size = s;}
    void SetInstall_type(CDROM_Install_type it){install_type = it;}
    void Run(){cout << "CD_ROM开始运行!" << endl;}
    void Stop(){cout << "CD_ROM停止运行!" << endl;}
};
class Computer{
private:
    CPU myCPU;
    RAM myRAM;
    CD_ROM mycdrom;
    unsigned int storage_size;//GB
    unsigned int bandwidth;//MB
public:
    Computer(CPU c, RAM r, CD_ROM cd, unsigned int s, unsigned int b);
    ~Computer(){cout << "析构了一个CD_ROM!" << endl;}
    void Run(){
    	mycpu.Run();
    	myram.Run();
		mycdrom.Run();
    	cout << "COMPUTER开始运行!" << endl;
	}
    void Stop(){
    	mycpu.Stop();
    	myram.Stop();
		mycdrom.Stop();
    	cout << "COMPUTER停止运行!" << endl;
	}    
};
COMPUTER::COMPUTER(CPU c, RAM r, CD_ROM cd, unsigned int s, unsigned int b):my_cpu(c), my_ram(r), my_cdrom(cd){
    storage_size = s;
    bandwidth = b;
    cout << "构造了一个COMPUTER!" << endl;
}
int main(){
    CPU a(P6, 300, 2.8);
    a.Run();
    a.Stop();
    cout << "*************************n";
    RAM b(DDR3, 1600, 8);
    b.Run();
    b.Stop();
    cout << "*************************n";
    CD_ROM c(SATA, 2, built-in);
    c.Run();
    c.Stop();
    cout << "*************************n";
    COMPUTER my_computer(a, b, c, 128, 10);
    cout << "*************************n";
    my_computer.Run();
    my_computer.Stop();
    cout << "*************************n";
    return 0;
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/767451.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号