「C++程序设计笔记」第四章 类与对象

本文深入讲解面向对象编程的核心概念,包括抽象、封装、继承和多态。通过实例演示类与对象的设计,构造函数和析构函数的使用,以及类组合的构造函数设计原则。此外,文章还介绍了UML的基本组成部分,以及结构体、联合体和枚举类的定义和应用。

第四章 类与对象

面向对象程序的基本特点

抽象
  • 对同一类对象的共同属性和行为进行概括,形成类。

首先注意问题的本质及描述,其次是实现过程或细节;

数据抽象:描述某类对象的属性或状态(对象相互区别的物理量);

代码抽象:描述某类对象的共有行为特征或具有的功能;

抽象的实现:类;

  • 例如:钟表抽象:

数据抽象:int hour,int minute, int second;

代码抽象:setTimer(),showTime()

class Clock{
    public:
        void setTime(int newH, int mewM, int newS);
        void showTime();
    private:
        int hour, minute, second;
}
封装

将抽象出的数据,代码封装在一起,形成类;

  • 将抽象出的数据成员、代码成员相结合,将它们视为一个整体。

目的:增强安全性和简化编程,使用者不必了解具体的实现细节,而只需通过外部接口,以特定的访问权限,来使用类的成员。

实现封装:类声明中的{}

class Clock{
    public:
         void setTime(int newH, int mewM, int newS);
         void showTime();
    private:
         int hour, minute, second;
};
继承

在已有类的基础上进行扩展形成新的类。

详见第七章;

即增加新的属性和行为

多态

详见第八章;

类与对象

类和对象的定义

设计类就是设计类型

此类型的“合法值”是什么?

此类型应该有什么样的函数和操作符?

新类型的对象改如何被创建和销毁?

如何进行对象的初始化和赋值?

对象作为函数的参数如何以值传递?

谁将使用此类型的对象成员?

类定义的语法

类内初始值

private:

int x = 0;

  • 共有成员,私有,保护成员;

private可省略;

  • 对象定义语法:

如:Clock myClock;

  • 类的成员函数

在类中声明函数原型;

可以在类外给出函数体实现,并在函数名前使用类名加以限定;

也可以直接在类中给出函数体,形成内联成员函数;

允许声明重载函数和带默认参数值的函数;

  • 内联成员函数

为了提高运行时的效率,对于较简单的函数可以声明为内联形式。

内联函数体中不要有复杂结构(如循环语句和switch语句);

在类中声明内联成员函数的方式:

将函数体放在类的声明中

使用inline关键字
类和对象的程序举例
  • 还是钟表类
class Clock{
    public:
         void setTime(int newH, int mewM, int newS);
         void showTime();
    private:
         int hour, minute, second;
};
  • 成员函数的实现
void Clock::setTime(int newH, int mewM, int newS){
    hour = newH;
    minute = newM;
    second = mewS;
}
void Clock::showTime(){
    cout << hour << ":" << minute << ":" << second;
}

区别在于类名;

  • 对象的使用
int main(){
    Clock myclock;
    myClock.setTime(8, 30, 30);
    myClock.showTime();
    return 0;
}

构造函数

构造函数的基本概念
  • 类中的特殊函数

  • 用于描述初始算法

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

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

不能定义返回值类型,也不能有return语句

可以有形式参数,也可以没有

可以使内联函数

可以重载

可以带默认参数值
构造函数的调用时机
在对象创建时被自动调用

例如 :Clock myClock(0,0,0)
默认构造函数
调用时可以不需要实参的构造函数
     
     参数表为空的构造函数

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

Clock(); Clock(int newH=0, ...)

以上两个默认构造函数不能在类中同时出现;将产生编译错误;

如果完全没有定义构造函数:编译器将自动生成一个默认构造函数

叫做隐含生成的构造函数

参数列表为空,不为数据成员设置初始值;

如果类内定义了成员的初始值,则使用类内定义的初始值;

如果没有,则以默认方式初始化;

基本类型的数据默认初始化的值是不确定的。

还可以:Clock()=default; //指示编译器提供默认构造函数

例子
class Clock{
    public:
         Clock(int newH, int mewM , int newS); //构造函数
         void setTime(int newH, int mewM, int newS);
         void showTime();
    private:
         int hour, minute, second;
};
// 构造函数的实现:
Clock::Clock(int mewH, int newM , int newS):
    hour(newH), minute(newM), second(newS){ //初始化列表,比在函数体中赋值效率更高
    }

一般:

Clock::Clock():hour(0), minute(0), second(0) //默认构造函数

一个类包含构造函数和默认构造函数;

为了: Clock c1(3, 4,5) 和 Clock c2;

委托构造函数

委托函数们只是形参不同,初始化列表不同;

为了减少重复;

回顾Clock类的两个构造函数;

例如:Clock():Clock(0, 0, 0){}

复制构造函数

用已经存在的对象初始化新对象

用已有对象的引用作为构造函数的参数;

默认复制构造函数实现两个对象的数据成员之间的一一对应的复制;很多时候都令人满意了

复制构造函数定义

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

…待补充;

print是值引用,每次执行print的时候,都会调用复制构造函数把实参赋值给形参。
例子

析构函数

析构函数

消亡时调用;

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

目前仍未有太多的关于占用的体会;

如果没有定义析构函数,编译器也会自动产生默认的,其函数体为空;

原型: ~类名();

析构函数没有参数和返回类型;

类的组合

部件组装思想;

组合的概念
类中的成员是另一个类的对象;

可以在已有的抽象的基础上实现更复杂的抽象;
类组合的构造函数设计

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

声明形式

类名::类名(对象成员所需的形参, 本身成员形参):

       对象1(参数), 对象2(参数), …

{
//函数体其他语句
}

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

    • 成员对象构造函数调用顺序:按对象成员的定义顺序,先声明先构造

    • 初始化列表中未出现的成员对象,调用默认构造函数(即无形参的)初始化;

  • 处理完初始化列表之后,再执行构造函数的函数体;

例子

在下面的主函数:

int main(){
    Point myp1(1,1), myp2(3, 4); //建立Point类对象
    Line line(myp1, myp2);  //建立Line类对象
    Line line2(line);       //利用拷贝构造函数建立一个新对象
}

关于Point类复制构造函数调用:

myp2 --> myp1 

调用两次

顺序由后到前

然后进入Line构造函数:

是两次调用Line复制构造函数;

顺序取决于定义顺序

接着进入Line构造函数函数体:

计算线段长度并返回

接着是用line初始化line2:

调用line复制构造函数;

然后又进到Point的复制构造函数;初始化p1和p2 , 又是两次调用;

然后才进入line复制构造函数的函数体;

其实自己debug看看就好

前向引用声明
由于类应该先声明,后使用;

如果需要在某个类的声明之前,引用该类,则应进行前向引用声明。

前向引用声明只为程序引入一个标识符,但具体声明在其他地方。

就:Class B;这样;

注意事项
在提供一个完整的类声明之前,不能声明该类的对象,也不能在内联成员函数中使用该类的对象。

当使用前向引用声明时,只能使用被声明的符号,而不能涉及类的任何细节。

如使用class A{ B x; } // 错误

允许的如:class A{public: void f(B, b);};

UML简介

UML三个基本部分
事物(Things)

关系(Relationships)

图(Diagrams)

点线面关系;

举例:

类图

  • Clock类的完整表示:
Clock
- hour: int
- minute:int
- second:int
+ showTime():void
+setTime(newH:int=0, newM:int=0, newS:int=0):void
  • Clock类的简洁表示
Clock

对象图

和类图差不多

依赖关系

类A使用类B或类A依赖类B

作用关系——关联

包含关系——聚集

继承关系——泛化

注释

以上图标:
在这里插入图片描述

@startuml

Call <|-- AudioCall : Generalization(泛化)
Call <|… CallImpl : Realization(实现)
Call <-- Beat : Dependency(依赖)

@enduml

@startuml

Customer Product : Association(关联)(这是双向关联)
Customer “1”–>“1” Address : has(单向关联)
Node “1”–>“1” Node : contains(自关联)

@enduml

@startuml

Company o-- Employee : Aggregation(聚合)
Company *-- Department : Composition(组合)

@enduml

结构体

结构体是一种特殊形态的类。

与类的唯一区别:

     类的缺省访问权限是private,结构体的是public
什么时候用结构体而不是用类
  • 定义主要用来保存数据而没有什么操作的类型。

  • 人们习惯将结构体的数据成员设为公有,因此这时用结构体更方便。

结构体在C++中用的并不多,一般用来与C兼容;

结构体定义

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

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

结构体的初始化

如果:

一个结构体的全部数据成员都是公共成员;

没有用户定义的构造函数;

没有基类和虚函数(第七章介绍)

这个结构体的变量可以这样初始化:

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

然后使用也很简单;

联合体

不是很常用,但很有用;

目的是:存储空间的共用;

定义形式

定义

union 联合体名称{
    公有成员
protected:
    保护成员
private:
    私有成员
};

特点:

成员共用同一组内存单元

任何两个成员不会同时有效;

按照占用空间最多的成员分配空间;

无名联合

union{
    int i;
    float f;
}

//在程序中可以这样使用:
i = 10;
f =1.3;

如此对i赋的值被f赋值冲掉了;

枚举类

也称为强类型的枚举

定义语法

enum class 枚举类名:底层类型{枚举值列表};

默认的底层类型是int

优势
  • 强作用域;例:使用Type的枚举值General: Type::General; 这样就不会重名了

  • 转换限制;指枚举对象不可以与整形隐式转换。

  • 可以指定底层类型; 例: enum class Type:char{General, Light, Medium, Heavy};

还无法直接比较不同的枚举类;

部件组装

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值