C++ 语法基础(四)

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

抽象过程:计算机的工作建立在抽象的基础上

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

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

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

面向对象程序设计:解决问题寻找帮手;可以解决大问题;

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

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

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

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

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

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

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

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

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

DoubleArray库的设计与实现

库的设计:

  • 数组的存储:需要一块保存数组元素的空间
  • 数组的操作:给元素赋值;访问元素的值;给元素分配空间;释放空间

将函数放入结构体

原有库的缺点:

  • 不能直接下标访问
  • 需要使用者关心空间分配问题
  • 函数名容易出现冲突
  • 使用相当笨拙

改进方案:将函数放入结构体

好处:减少了函数所需的参数,同时解决了可能存在的函数名冲突的问题

将函数放入结构体是从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指针

每个成员函数都有一个隐藏的指向本类型的指针形参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指针。

填空题

1.

为以下的类实现一个add函数:

class SampleClass
{
    int a;
public:
    SampleClass add()
    {
        /*
        此处是你需要填的内容
        这个函数实现了将变量a增加1的功能
        最后返回增加1前的对象(不要求地址相同)
        */
        SampleClass temp=*this;
        a++;
        return temp;
    }
}

对象的构造与析构

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

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

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

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

构造函数 

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

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

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

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

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

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

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

析构函数

析构函数在销毁对象时,完成一些善后工作(如,归还分配给指针的空间),由编译系统自动调用

析构函数与构造函数名字相同,但它前面加一个波浪号(~),析构函数没有参数、返回值,也不能重载。

若定义类时没有定义析构函数,编译系统会自动生成一个缺省的空析构函数

class SampleClass
{
    int value;
    int *pointer;
public:
    SampleClass(int a,int b):value(a)
    {
        pointer=new int(b);
    }
    ~SampleClass()
    {
        delete pointer;
    }
}

可以利用析构函数与构造函数来验证对象的生命周期

//为下面的类构建一个析构函数,其中指针`arrays`指向了一个数组。
#include <bits/stdc++.h>
using namespace std;

class SampleClass
{
private:
    const int key;
    string *value;
	int *arrays;
    
public:
	SampleClass(int key_, string val_, int n) : key(key_)
	{
		value = new string(val_);
		arrays = new int[n];
	}

	~SampleClass()
	{
		delete value;
		delete [] arrays;
	}

	void print(){
		printf("%d\n", key);
		cout << *value << endl;
	}
};

int main(){
    SampleClass a(321, "hello world", 5);
	a.print();
}

运算符重载 

什么是运算符重载

使系统内置的运算符可以用于类类型

例如:类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;
}
//题目描述:
//补全Int类,使得程序的输出为:
//1
//2
//3
//4
//5
#include <iostream>
using namespace std;
class Int
{
    static int addtime;

public:
    Int(int x) {}
    friend int operator+(const Int &r1, const Int &r2)
    {
        return ++addtime;
    }
};
int Int::addtime = 0;

int main()
{
    Int a(1), b(1), c(1);
    cout << a + a << endl;
    cout << b + b << endl;
    cout << b + c << endl;
    cout << c + c << endl;
    cout << a + a << endl;
}

组合

  • 对象成员
  • 聚集关系,是一种部分和整体的关系,即has-a的关系
  • 组合的目的:简化创建新类的工作,让创建新类的程序员不需要过多地关注底层的细节,可以在更高的抽象层次上考虑问题,有利于创建功能更强的类。

继承

c++派生类定义的格式如下:

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

继承方式可以是public、private和protected

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

 

 

 多态性

  • 多态性有两种实现方式:编译时的多态性和运行时的多态性
  • 运算符重载和函数重载属于编译时的多态性
  • 虚函数属于运行时的多态性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C++是一种通用的编程语言,它支持面向对象的编程风格,并且具有强大的系统编程能力。下面是C++的一些基础语法: 1. 注释:在C++中,注释可以用来解释代码,以便于其他人阅读和理解。单行注释使用双斜线(//),多行注释使用斜线和星号(/* ... */)。 2. 标识符:标识符是变量、函数、类等的名称。标识符由字母、数字和下划线组成,并且以字母或下划线开头。 3. 变量:在C++中,需要声明变量来存储数据。变量的声明包括类型和名称。例如,int表示整数类型,而float表示浮点数类型。 4. 数据类型:C++提供了多种数据类型,包括整型(int、short、long)、浮点型(float、double)、字符型(char)、布尔型(bool)等。 5. 运算符:C++支持各种运算符,例如算术运算符(+、-、*、/)、关系运算符(==、!=、<、>)、逻辑运算符(&&、||、!)等。 6. 控制流语句:C++提供了多种控制流语句,例如条件语句(if-else)、循环语句(for、while、do-while)、跳转语句(break、continue、return)等。 7. 函数:函数是可重用的代码块,用于执行特定的任务。函数由函数头和函数体组成,函数头包括返回类型、函数名和参数列表。 8. 类和对象:C++是面向对象的语言,支持类和对象的概念。类是一种用户定义的数据类型,用于封装数据和方法。对象是类的实例,可以通过调用对象的方法来操作数据。 这只是C++语言的一些基础语法,还有很多其他的概念和特性。如果你对某个特定的主题有更深入的兴趣,我可以为你提供更详细的信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小馨馨的小翟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值