C++基础——类和对象

本文深入介绍了C++中的面向对象编程概念,包括从过程化到面向对象的转变,类和对象的定义与使用,以及构造函数和析构函数在对象生命周期中的作用。此外,还详细阐述了运算符重载的原理和实现,以及继承和多态的基本概念,强调了代码重用、实现隐藏和灵活性在面向对象设计中的重要性。
摘要由CSDN通过智能技术生成

C++基础——类和对象

(一)面向对象

1. 从过程化到面向对象(上)

知识点
抽象过程:计算机的工作建立在抽象的基础上
在这里插入图片描述

计算机层面的抽象:从计算机硬件,到机器语言,再到高级语言

当出现一个抽象的任务时,需要程序员在机器模型与实际问题模型之间建立联系,按照计算机的结构去思考

  • 过程化程序设计:每个步骤用语句代替;适合解决小问题;

  • 面向对象程序设计:解决问题寻找帮手;每个帮手就是一个对象,可以解决大问题;

2. 从过程化到面向对象(下)

对象与变量:都是程序中解决问题的工具;后者是语言已经设计好的工具,前者是程序员自己创建的工具

面向对象的程序设计为程序员提供了创建工具的功能

解决问题过程:

  1. 考虑需要的工具;
  2. 创建、收集工具;
  3. 设计利用工具解决问题的过程

在这里插入图片描述

3. 面向对象的程序设计的特点

代码重用:已有的工具能给其他程序员使用

实现隐藏:工具的使用者不需要知道工具的内部实现方式

继承:在已有工具的基础上加以扩展,形成一个功能更强的工具

多态:对不同对象发出同一指令,不同对象有不同行为

(二)类

将函数放入结构体是从C到C++的根本改变!
在这里插入图片描述
类定义与声明:

class ClassName
{
private:
    //私有数据成员和成员函数
public:
    //公有数据成员和成员函数
};

私有成员(private):只能由类的成员函数调用,私有成员被封装在一个类中,类的用户是看不见的

公有成员(public):类的用户可以调用的信息,是类对外的接口

private和public的出现次序可以是任意的,可以反复出现多次,成员还可以被声明为protected(此声明在之后的章节会进行解释)

类的设计:
数据成员和成员函数:

根据所需保存的信息设计数据成员
根据行为设计成员函数

访问特性:

数据成员一般是私有的
成员函数一般是公有的
成员函数实现时分解出的小函数是私有的
在实际工程中,我们可以将接口和实现分开:

  • 类的定义写在接口文件中
  • 成员函数的实现写在实现文件中
  • 某些简单的成员函数的定义可以直接写在类定义中
  • 在类定义中定义的成员函数被认为是内联函数

举例:有理数类的设计
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

(三)使用对象

类与对象的关系:类型与变量的关系

对象定义
直接在程序中定义某个类的对象,各种定义方式与行为与各种数据类型相似。

//存储类别  类名  对象列表;
DoubleArray arr1, arr2;
static DoubleArray  array[10];
Rational  *rp;
rp = new Rational;     
delete  rp;
rp1 = new Rational[20]; 
delete [] rp1;

对象的使用
对象的使用是使用它的成员:公有成员可以被任何函数使用,私有成员只能被自己的成员函数使用。

成员的引用:

/*
对象名.数据成员名
对象指针->数据成员名    
对象名.成员函数名(实际参数表) 
对象指针->成员函数名(实际参数表) 
*/
arr1.storage = 0; 
rp->num = 1;
arr1.insert(5, 3.7);   
rp->add(r1, r2);

每个成员函数都有一个隐藏的指向本类型的指针形参this,指向控制对象

如对函数:

void create(int n, int d) { num = n; den = d;}

经过编译后,实际函数为:

void create(Rational *this, int n, int d) { this->num = n; this->den = d;}

通常,在写成员函数时可以省略this,编译时会自动加上它们。
在这里插入图片描述

如果在成员函数中要把对象作为整体来访问时,可以显式地使用this指针。*this 代表当前变量

(四)构造与析构

变量可以在定义时赋初值,对象能赋初值吗?怎么赋?

将赋初值的过程写成一个函数,让计算机在定义对象时执行该函数,这个函数称为构造函数

同理,有时在对象消亡前也有些工作要做,可将该过程写成一个函数,让计算机在对象消亡时执行该函数,这个函数称为析构函数

构造函数和析构函数是特殊的成员函数

1. 构造函数

构造函数说明了定义对象时,如何为对象赋初值,由系统在定义对象时自动调用。构造函数的名字必须与类名相同

如果没有给类定义构造函数,编译系统会自动生成一个缺省的构造函数。它只为对象分配存储空间,空间中的内容为随机数。

构造函数可以有任意类型的参数,也可以不带参数,但不能具有返回类型。构造函数可以重载。

class SampleClass
{
public:
    SampleClass(/*...*/){/*...*/}
}

不带参数的构造函数称为默认的构造函数,一般每个类应该有一个缺省的构造函数。

对象定义的一般形式:类名 对象名(实际参数表);

实际参数表必须与某个构造函数的形式参数表相对应,除非这个类有一个构造函数是没有参数的,那么可以用:类名 对象名;

2. 析构函数

在这里插入图片描述

(五)运算符重载

什么是运算符重载?
使系统内置的运算符可以用于类类型

例如:类A的对象a1、a2、a3,可以执行
a3 = a1 + a2;

运算符重载的方法
运算符重载就是写一个函数解释某个运算符在某个类中的含义

关键问题
如何使编译器能自动找到重载的这个函数

解决方案
函数名必须要体现出和某个被重载的运算符的联系。
C++中规定,重载函数名为 operator@ . 其中,@为要重载的运算符

  • 重载“+”运算符,该重载函数名为 operator+
  • 重载赋值运算符,函数名为 operator=

函数原型 运算符的重载不能改变运算符的运算对象数、形式参数个数(包括成员函数的隐式指针this)及类型与运算符的运算对象相符
返回值与运算结果值类型一致

成员函数vs全局函数
大多数运算符的重载函数可以写成成员函数,也可以写成全局函数
重载成全局函数 函数原型与运算符与完全相符 最好将此函数设为友员函数
重载成成员函数 形式参数个数比运算符的运算对象数少1。这是因为成员函数有一个隐含参数this,this是第一个形式参数

举例
为rational类增加“+”和“*”以及“==”比较的重载函数,用以替换现有的add和multi函数

方案一:重载成成员函数

class Rational
{
private:int num;int den;void ReductFraction();
public:Rational(int n = 0, int d = 1)
    {
        ​ num = n;
        ​ den = d;
    }

    ​ Rational operator+(const Rational &r1) const;
    ​ Rational operator*(const Rational &r1) const;bool operator==(const Rational &r1) const;void display()
    {
        ​ cout << num << '/' << den;
    }
};

函数实现

Rational Rational::operator+(const Rational &r1) const
{
    Rational tmp;
    tmp.num = num * r1.den + r1.num * den;
    tmp.den = den * r1.den;
    tmp.ReductFraction();
    return tmp;
}

Rational Rational::operator*(const Rational &r1) const
{
    Rational tmp;
    tmp.num = num * r1.num;
    tmp.den = den * r1.den;
    tmp.ReductFraction();
    return tmp;
}

bool Rational::operator==(const Rational &r1) const
{
    return num == r1.num && den == r1.den;
}

方案二:重载成友员函数

class Rational
{friend Rational operator+(const Rational &r1, const Rational &r2);friend Rational operator*(const Rational &r1, const Rational &r2);friend bool operator==(const Rational &r1, const Rational &r2);
private:int num;int den;void ReductFraction();
public:Rational(int n = 0, int d = 1)
    {
        num = n;
        den = d;
    }void display() { cout << num << '/' << den; }
};

函数实现

Rational operator+(const Rational &r1, const Rational &r2)
{
    Rational tmp;
    tmp.num = r1.num * r2.den + r2.num * r1.den;
    tmp.den = r1.den * r2.den;
    tmp.ReductFraction();
    return tmp;
}

Rational operator*(const Rational &r1, const Rational &r2)
{
    Rational tmp;
    tmp.num = r1.num * r2.num;
    tmp.den = r1.den * r2.den;
    tmp.ReductFraction();
    return tmp;
}

其他函数实现略

重载后有理数类的使用

int main()
{
    Rational r1(1, 6), r2(1, 6), r3;
    ​r3 = r1 + r2;
    ​r1.display();
    cout << " + ";
    r2.display();
    ​cout << " = ";
    r3.display();
    cout << endl;
    ​r3 = r1 * r2;
    ​r1.display();
    cout << " * ";
    r2.display();
    ​cout << " = ";
    r3.display();
    cout << endl;return 0;
}

(六)继承与多态

组合:对象作为类的数据成员
聚集关系,是一种部分和整体的关系,即has-a的关系
组合的目的:简化创建新类的工作,让创建新类的程序员不需要过多地关注底层的细节,可以在更高的抽象层次上考虑问题,有利于创建功能更强的类
在这里插入图片描述
在这里插入图片描述
知识点
c++派生类定义的格式如下:

class 派生类名:继承方式 基类名{
	新增加的成员声明;
};

继承方式可以是public、private和protected,大多数是public,protected是一类私有成员,但是派生类可以访问

派生类中基类成员的访问特性

public继承protected继承private继承
基类public成员publicprotectedprivate
基类protected成员protectedprotectedprivate
基类private成员不可访问不可访问不可访问

注:不可访问指派生类不可访问基类成员,private则指基类成员作为派生类的private成员

多态性有两种实现方式:编译时的多态性(静态联编,在编译的时候就知道是哪个函数)和运行时的多态性(动态联编)
运算符重载和函数重载属于编译时的多态性
虚函数属于运行时的多态性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值