一篇文章带你走进类的世界(上)——类与对象

前言

本文将用最通俗易懂的语言讲解专业的有关 C++ 类的知识,准备好了吗?让我们上车。

什么是类

在 C++ 中,它似乎就是一个关键字 class,但它所包含的功能可太多太多了,现在就让我们走进类的世界吧。

在这之前,先了解一点小知识。

面向对象程序的基本特点

一、抽象

抽象,是指对具体的事或物进行概括,从而抽象一类对象的意义并进行描述的过程。常见的有数据抽象行为抽象。前者用来描述某类对象的特性,后者用来描述某类对象的功能,其中,变量就是一种很典型的数据抽象,函数就是一种典型的行为抽象。

数据抽象(例子):

int a=1;
double b=3.14;
char c='a';

行为抽象(例子):

void add(int &x,int &y)
{
    x+=y;
}

二、封装

封装,即将抽象得到的数据或行为相结合,形成的一个整体,如结构体就是一种封装。

封装(例子):

struct Node{//一个整体
    int x,sum;//数据抽象
    void init(int a)//行为抽象
    {
        x=a;
        sum+=a;
    }
};

三、继承

继承,即将基本概念传递给另一个从而形成特殊概念的过程,在 C++ 中典型的是类的继承,这个我们在后面会讲到。

四、多态

多态,即通过一种程序执行多种类型的操作的能力,这个在此不多讲,只会在提到虚函数的时候提一嘴,其他的我以后单独出一篇文章讲一讲。

类就是一种封装形式,同时兼具继承的本领,同时在继承的过程中也会有多态的使用,可以说,类几乎就是一个结合体。

首先我们从简单的开始。

一、类的定义方法

在 C++ 中,要声明一个类的方法很简单,像结构体一样,具体看下面:

class A{//class:声明一个类,A:类名
    ...
};

二、类的成员

类的成员一共分为三种:公有成员、私有成员、保护成员。分别用关键字 publicprivateprotected 来声明,见示例:

class A{
    public://公有成员
        ...
    private://私有成员
        ...
    protected://保护成员
        ...
};

你可以这么理解:公有成员就像是你们一家的外观,比如说门、墙这些外人可以随便看的,所以公有成员可以在程序的任何地方被调用(当然前提是必须在类声明了之后才可以调用)。私有成员就像是你爸的私房钱,无论跟谁都不能说,所以私有成员只能在类内调用(当然这不是绝对的,后面会讲一种特殊情况)。保护成员则更像你家的传家宝,不能给别人,只能给自家人,所以保护成员也只能在类内调用,当然,保护成员和私有成员有一定区别,具体会在后面讲到。

三、类的对象

现在有各种各样的变量和函数了,我们要怎么去调用他们呢?很简单,像结构体一样:

class A{
}a;

或者写成这样:

A a;

在调用时就用 . 来调用,即:

a.x;

如果是指针就用 -> 来调用:

A *p;
p->x;

四、类的成员函数

实际上就是在类内写一个函数,比如说像下面这样:

class A{
    public:
        void sum()
        {
            ...
        }
};

如果你想装一下呢,可以选择这么写:

class A{
    public:
        void sum();
};
void A::sum()
{
    ...
}

当然,这两种写法没有什么本质上的区别,而且因为这个函数是在类内声明的,所以它可以随便调用类内的私有成员和保护成员

五、构造函数与析构函数

(一)构造函数

在讲构造函数之前,我们先来大致了解一下在电脑内存里你定义了一个类的全过程。

首先,你的电脑肯定要给你的类单独开辟一块内存出来以存放你写的类,然后对这块空间里面的所有变量的值进行一个初始化(C++ 自带的),即初始化为 0 0 0

而构造函数则是把这个初始化具象化的一类函数,通俗来讲,就是构造函数会在你生成一个类之后自动调用并执行构造函数里面的内容。

1.构造函数的定义

首先我们肯定要定义一个类:

class A{
	private:
		int a; 
}; 

然后我们要写一个构造函数,构造函数的定义注意一下几点:

  1. 构造函数的名字必须与类的名字相同
  2. 构造函数是系统默认的函数,不能是某种类型(即它不能是 intchardouble 等类型,它就是一个函数),但是可以带参数。
  3. 如果不写构造函数,系统会自动带一个默认构造函数,当然,你也可以声明这个默认构造函数(只不过没太大用)。
  4. 构造函数必须写在公共成员里面。
  5. 构造函数可以定义多个,但不能重复,也就是说,多个构造函数里面的参数类型和个数不一样

举个例子:

class A{
	public:
		A();//默认构造函数,约等于没用
		A(int x)//自定义的构造函数
		{
			a=x;
		} 
	private:
		int a; 
}; 

这时,我要声明一个类就要这样写:

A n(6);

这表示我调用了构造函数 A(int x),当然也可以写成这样:

A n();

这样表示调用那个默认构造函数。

当然,如果你没有构造函数,那个括号就不用加,因为系统自己会有一个默认的构造函数,那个构造函数不需要你写任何东西手动去调用。

2.复制构造函数

复制构造函数是构造函数的一种,主要作用是用一个已经定义了的类取定义另一个类,听着有点绕口,我们来看代码:

class A{
	public:
		A(int x)//正常的构造函数 
		{
			a=x;
		}
		A(A &p)//复制构造函数 
		{
			a=p.a;
		}
	private:
		int a; 
}; 

不难发现:复制构造函数其实就是一个把参数换成类的引用的函数,那么在定义类的时候就应该这么写:

A n(1);
A m(n);

这是你会发现:na 1 1 1ma 也是 1 1 1

3.移动构造函数

这个概念比较抽象,而且不常用,所以我就简单提一嘴。

在 C++11 里面有两个定义:左值和右值,通俗来讲,左值就是复制语句左边的值,右值就是赋值语句右边的值。之后又有两个新定义:左值引用和右值引用。左值引用指对在内存中持久存在的变量的引用,用 & 来定义,右值引用则指对在内存中短暂存在的变量的引用,用 && 来定义。

举几个例子你就知道了:

int a=1;
int &l=a;//正确,因为 a 是一个变量,在内存中持久存在
int &l=a*a;//错误,因为 a*a 是一个运算,在内存中只会短暂存在
int &&r=a;//错误,因为 a 是一个变量,在内存中持久存在
int &&r=a*a;//正确,因为 a*a 是一个运算,在内存中只会短暂存在
int &ll=1;//错误,1 是一个数,在内存里并没有实际空间,只会短暂存在
int &&rr=1;//正确,1 是一个数,在内存里并没有实际空间,只会短暂存在

移动构造函数就是利用右值只能短暂存在的特性,将构造函数内的参数变为右值引用,从而减少内存的消耗,但是因为涉及到底层问题,所以很少用,这里就只是提一嘴。

(二)析构函数

析构函数,一种非常没用的操作,其实说白了就是用来清理类的调用空间的,一般情况下你不写系统会自带一个析构函数,当然你也可以自己声明:

class A{
	public:
		~A();
}; 

但是我相信应该没有人没事找事干声明一个没有用的函数吧……

六、前向引用声明

我们知道,在 C++ 中要调用一个函数要先声明它,但是我们难免会出现下面这种情况:

void A() 
{
	...
	B();
}
void B()
{
	...
	A();
}

那这时你把哪个函数放在前面都会报错,那该怎么办呢?

很简单,我们只需要提前声明这个函数就行了:

void B(); 
void A() 
{
	...
	B();
}
void B()
{
	...
	A();
}

类也是如此,你也可以写成这样:

class B; 
void A()
{
	...
	B x;
	...
};
class B
{
	...
};

只不过一般情况下我们不这么写罢了。

预告:下一篇文章:《类的继承》。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值