C++教程(三):类与对象

+参考书: 郑莉(清华大学)C++ 语言程序设计 (第3版)

3 类与对象

3.1 面向对象编程的基本特点

抽象

封装:

继承:

多态:

3.2 类与对象

构建、封装独立的功能模块是提高程序开发速度、提高程序效率的有效手段;函数是对运算的封装,库是对函数的封装。

类是对逻辑上相关的函数和数据的封装,不仅包括函数,也包括函数要处理的数据类型;是数据抽象行为抽象而这结合的。

3.2.1 C语言中的类

实际上C语言中就已经有类和对象了,但是C中的类是不开放的,并且不提供自定义类的接口。的全称是类型,而我们已经接触过intbooldoublechar,或者自定义的结构体;当我们使用这些类声明一个对象时,例如

int i = 1;
bool a = false;
double pi = 3.14;
char ch = 'c';
struct student{
    char name[20] = 'wuyao';
    long int id = 1150250107;
    char gender = 'm';
    char email[32] = 'wuyao1997@qq.com';
}wuyao;

类型的实例就是对象!)

我们不仅定义了这些变量的值,并且还隐式的定义了对于这些对象可以采用的操作,例如整型变量可以加减乘除,字符(数组)变量就不能加减,只能进行连接、拷贝等操作。

3.3.2 C++中类
3.2.2.1 定义

创建类

语法形式为

class classname
{
    public:
    	外部接口;
    protected:
    	保护型成员;
    private:
    	私有成员;
};

时钟为例,可初步的抽象为

  • 数据抽象:时int hour、分int minute、秒int second
  • 行为抽象:显示时间setTime(int newh, int newm, int news);现实时间showTime();

当然这只是初步的抽象,可以进一步细化数据抽象和行为抽象,因为一般不允许时间为42时99分-87秒这种格式,用户也希望时钟可以现实多种格式的时间,或者有定时功能等等,但这里暂不讨论;于是时钟类可以写为

class Clock
{
	public:
    	void setTime(int newh, int newm, int news);
    	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 << endl;
}

创建对象

使用类名来实例一个对象,形如

Clock myclock;		// 类型名 类型变量名
3.2.2.2 访问控制

类的成员类型上分为:

  • 数据成员:描述对象的属性
  • 函数成员:描述对象的行为

以时钟为例,一般用户只需要能显示时间即可,如果有必要可以设定下时间;而时间的显示样式一般人就没有必要关心了;因此需要设定不同的访问权限,避免一般用户在不了解函数内部情况时,误操作导致程序崩溃。

访问控制属性

  • 公有类型public
  • 私有类型private
  • 保护类型protected

公有类型成员定义了类的外部接口,使用public关键字声明,在类外仅能访问类的共有成员。

私有类型成员只能被本类的成员函数访问,来自类外部的任何访问都是非法的。若不声明,默认为私有类型函数。

保护类型成员的性质和私有成员的性质相似,差别在于继承过程中对产生的新类影响不同。


访问类的成员

  • 成员数据:
    • 外部访问格式:对象名.变量名,例如myclock.hour(这里当然是非法的,不能访问类的私有成员数据)
    • 内部访问格式:直接使用变量名,例如hour
  • 成员函数:
    • 外部访问格式: 对象名.函数名(参数列表),形如myclock.showTime()
    • 内部访问格式:直接使用函数名,例如showTIme()
3.2.2.3 成员函数

函数的具体实现一般放在类定义的外部,并用类名作为命名空间,这部分前面以及举例。

带默认参数的成员函数

规则与普通函数相同,形如

void Clock::setTime(int newh = 0, int newm = 0, int news = 0)
{
	hour = newh;
    minute = newm;
    second = news;
}

内联成员函数

  • 隐式声明:将函数体直接放在类体内,形如

    class Clock
    {
    	public:
        	void setTime(int newh, int newm, int news);
        	void showTime()
            {	cout << hour << ":" << minute << ":" << second << endl;	}
        private:
        	int hour, minute, second;
    };
    
  • 显示声明:使用inline显示声明,位置在函数返回值类型之前,形如

    inline void Clock::showTime()
    {
        cout << hour << ":" << minute << ":" << second << endl;
    }
    
3.2.2.4 时钟类的完整程序
//3_2.cpp
#include <iostream>

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 << endl;
}


int main()
{
	Clock myclock;
	cout << " First time set and output : " << endl;
	myclock.setTime();
	myclock.showTime();
	cout << " Second time set and output : " << endl;
	myclock.setTime(8,30,30);
	myclock.showTime();
	return 0;
}

编译后运行结果

 First time set and output : 
0:0:0
 Second time set and output : 
8:30:30

3.3 构造函数与析构函数

在使用来声明对象时,可以对其数据成员赋值,成为对象的初始化。在特定对象使用结束时,需要进行一些内存清理工作。这两项工作需要两个特殊的成员函数来完成,分别为构造函数和析构函数。

3.3.1 构造函数

构造函数是用来初始化对象的,那么初始化一个基本对象需要:

  • 分配内存单元
  • 写入变量的初始值

但是程序员构建的类可能千变万化,编译器有时不能自动生成代码来的初始化,需要程序员来编写初始化程序,即构造函数。

  • 构造函数的作用是初始化一个对象;
  • 若程序员不编写构造函数时,编译器会自动生成构造函数,一般情况是可以正常行使初始化的功能;
  • 构造函数在对象被创建时会被自动调用;
  • 构造函数的函数名与类名相同,并且没有返回值;
  • 一般是公有函数;

例如时钟类中写一个构造函数,其声明为Clock (int newh, int newm, int news);,函数实现为

Clock::Clock(int newh, int newm, int news)
{
	hour = newh;
    minute = newm;
    second = news;
}

此时可以这样创建一个时钟对象

Clock myclock(0, 0, 0);

作为类的成员函数,构造函数也可以有默认参数,可以重载,可以为内联函数;形如

class Clock
{
	public:
    	Clock(int newh, int newm, int news);
    	Clock()
        {	hour = 0; minute = 0; second = 0;}
    	void setTime(int newh = 0, int newm = 0, int news = 0);
    	void showTime();
    private:
    	int hour, minute, second;
};
3.3.2 拷贝构造函数

可以认为拷贝构造函数是构造函数的一种重载形式。

现实生活中存在很多复制操作,C++中复制一个对象时,可以新建一个对象,然后将需要复制对象的数据成员的值一一提取出来,一一赋值给新的对象;但是这样太过于繁琐;于是创造了一个拷贝构造函数的概念,其作用就是使用一个已经存在的对象,去初始化同类的一个新对象。本质上是复制操作符的函数化表现。

  • 拷贝构造函数的形参是对本类对象的引用
  • 若程序员不编写,系统会默认生成一个拷贝构造函数,将被拷贝对象的每个数据成员的值都复制到新建立的对象中,即克隆

声明格式为

 类名(类名 & 对象名)

形如

class Ponit
{
    public:
    	Ponit(int xx=0, int yy=0) {X=xx;Y=yy;}	//构造函数
    	Ponit(Ponit &p)							//拷贝构造函数
        int GetX() {return X;}					//内联函数
    	int GetX() {return X;}
    private:
    	int X,Y;
};

实现格式为

类名::类名 (类名 & 对象名)
{
    函数体
}

形如

Ponit::Ponit(Ponit &p)
{
    X=p.X;
    Y=p.Y;
    cout << "拷贝构造函数被调用" << endl;
}

构造函数在以下情况被调用

  • 当用一个类初始化另一个类时

    int main()
    {
    	Ponit A(1,2);
        Ponit B(A);
        cout << B.GetX() << endl;
        return 0;
    }
    
  • 函数的形参是类的对象时,调用函数时,进行参数传递,实际上是拷贝一份传递到被调函数中

    void f(Ponit p)
    {
        cout << p.GetX() << endl;
    }
    
    int main()
    {
        Ponit A(1,2);
        f(A);
    }
    
  • 函数的返回值是类的对象时,从被调函数返回时也会进行拷贝传递

Ponit g()
{
    Ponit A(1,2);
    return A;
}

int main()
{
    Ponit B;
    B = g();
}

实例

  //3_3b.cpp
#include <iostream>
using namespace std;

class Ponit
{
    public:
    	Ponit(int xx=0, int yy=0) {X=xx;Y=yy;}	//构造函数
    	Ponit(Ponit &p)	;						//拷贝构造函数
        int GetX() {return X;}					//内联函数
    	int GetY() {return Y;}
    private:
    	int X,Y;
};

Ponit::Ponit(Ponit &p)
{
    X=p.X;
    Y=p.Y;
    cout << "拷贝构造函数被调用" << endl;
}

void f(Ponit p)
{
    cout << p.GetX() << endl;
}

Ponit g()
{
    Ponit A(1,2);
    return A;
}


int main()
{
	Ponit A(4,5);
	Ponit B(A);
	cout << B.GetX() << endl;
	f(B);
	B=g();
	cout << B.GetX() << endl;
	return 0;
}

编译执行


3.3.3 析构函数
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值