C++学习笔记(11)——初识类

在《C++学习笔记(1)——初识C++》中,大概总结了C++的特性是面向对象(oop),Bjarne Stroustrup最初将C++语言命名为带类的C语言。类是C++语言面向对象的集中体现。从今天开始,进入类的世界。

1.什么是类

类是一种将抽象转换为用户定义类型的C++工具。它将数据表示和操纵数据的方法组合成一个整体。类的基本思想是数据抽象和封装。

类的书写语法规则是:

1.类声明:以数据成员的方式描述数据部分,以成员函数(被称为方法)的方式描述接口函数。类声明用class关键字,一般写在h文件中。

class 类名称(通常类名称的首字母大写)

{

public:

     公有成员(外部接口)

private:

     私有成员 (只允许本类中的函数访问,而类外部的任何函数都不能访问)

protected:

     保护成员(与private类似,差别表现在继承与派生时)

};

2.类方法定义:描述如何实现类成员函数。通常写在对应的CPP文件中。

简单地说,类声明给出了类的框架,而方法定义给出了类的细节。

举个栗子

class Calculate

{

private:

      int a;

      int b;

public:

int add();

int sub();

int mul();

};

2.类成员访问控制

上述的格式中出现了public,private和protected三个关键字,这三个关键字正是限制和表明了类的成员的访问权限。类的一个优势在于数据隐藏,数据隐藏的意义在于不仅可以避免直接访问数据(在内存模型中总结过减少数据直接访问可以提高软件可靠性),另外还可以让用户不用关心数据的存储形式,因为数据在接口函数内部自行完成某些算法,我们只要知道接口就可以了。也正是鉴于此目标,类通常将数据项放在私有类型中,用private关键字修饰它们。这些私有的数据项不仅仅指变量,它也包括一些私有接口函数。而要开发给用户去访问的那些接口就要放在公有接口,用public修饰的后面了。

反过来,可以如此总结:

private下是类的私有变量和函数,只有类自己的成员函数可以访问;

public下是类的共有变量和函数,可以被外部访问;

protected:私密性介于private与public之间,记住它只是在类的派生类中可以访问即可。后面的笔记会再提到它。

3.类的方法实现

声明类之后的工作就是要提供类的方法定义,也即实现类成员函数。通常在cpp源文件中完成,定义成员函数唯一区别于普通函数的地方就是需要用作用域解析运算符::来关联类名称和函数名,使得可以在函数内用类的私有变量。

int Calculate::add()

{

      return a+b;

}

当然,也可以在类的声明中直接完成成员函数定义。这种情况下,函数默认是一个内联函数。这也要求函数的规模符合内联函数的特点,即短小精悍。

class Calculate

{

private:

      int a;

      int b;

public:

int add(){return a+b;}

int sub();

int mul();

};

如果不通过类的申明中(只class后的中括号中)定义接口函数来实现内联,可以用inline关键字声明它,但内联函数的实用要求每个文件中都有一个该函数定义,通常就把它写在了头文件中,于是你应该见到过下面的形式(都位于头文件中):

class Calculate

{

private:

      int a;

      int b;

public:

int add();

int sub();

int mul();

};

inline int Calculate::add()

{

      return a+b;

}

为了保证数据的安全,避免类的成员函数不修改对象的值,应该尽量将成员函数定义为const成员函数。定义一个的const成员函数时const关键字是放在括号后面,他的意思是这个函数不能修改调用它的对象的值。与返回值是否是常量无关。

int add() const;//声明于头文件的class声明中

int Calculate:add() const//定义于源文件中

{

      return a+b;

}

4.构造函数

如上述Calculate类中的a和b应该有初始化的操作,当然可以写一个专门的初始化函数,如init()函数来实现a和b的初始化。但是这就意味着每次创建类的对象后都要主动调用一次该init函数来完成初始化工作。实际中存在一种需求就是可以直接在创建类的对象的同时完成某些初始化工作。于是就有了构造函数。

构造函数的名字与类名相同,不含有返回类型,甚至不能有void修饰。按照上述需求,我们声明和定义一个构造函数:

Calculate(int a, int b); //声明于头文件的class声明中

Calculate::Calculate(int a, int b) //定义于源文件中

{

      m_a = a;

      m_b = a;

}

注意上述写法,为了将构造函数的形参和类的成员变量区分开,成员变量该改为了m_开头,这也是一种编程习惯。

默认构造函数是在未提供初始值时进行对象初始化的构造函数。当一个类没有提供任何的构造函数时,编译器会隐式地定义一个默认构造函数。以Calculate类为例,编译器提供的默认构造函数的形式应该是如下的;

Calculate::Calculate(){};

当然也可以显式的定义默认构造函数。但是,当显式地定义了构造函数后,编译器便不会再提供默认构造函数。而很多时候不能没有默认构造函数,因为经常需要定义一些临时的类对象,当这些对象不带有初始值时,需要默认构造函数进行初始化,所以我们通常需要显式地给出默认构造函数。

Calculate::Calculate() //定义于源文件中

{

      m_a = 0;

      m_b = 0;

}

我们还可以利用函数重载,实现多个构造函数;可以利用函数参数的默认值来实现某些初识默认值的情况。

5.析构函数

用构造函数创建对象之后,程序跟踪该对象直到其过期为止,当对象过期时,程序会自动调用一个特殊的成员函数完成清理工作。这个函数就是析构函数。例如,构造函数使用new来分配内存,那么需要析构函数使用delete释放内存。

析构函数的名称也很特殊:在类名前面加上~ 即可。与构造函数一样,析构函数也没有返回值类型和声明类型,与构造函数不同的是,析构函数没有参数。

~Calculate(); //声明于头文件的class声明中

Calculate::~Calculate() //定义于源文件中

{

}

在析构函数中,不存在类似构造函数中初始化列表的东西来控制成员销毁,析构的过程是隐式的。当程序员不显式地定义析构函数时,编译器会提供一个析构函数,这个析构函数的函数体为空。

大多数情况下,程序员都可以不定义析构函数,而直接使用编译器提供的析构函数,但是当构造函数中使用了动态申请内存的方式为对象的属性进行赋值时,需要程序员自己提供析构函数,因为编译器提供的析构函数不会释放堆内的内存。

一个对象被销毁,就会自动调用析构函数,析构函数销毁成员时,按照成员初始化的顺序逆序销毁。一个类有且只有一个析构函数,这就要求构造函数对于动态成员申请内存的方式是一致的。需要记住的是,析构函数的工作是清理对象,而不是删除对象。

6.拷贝构造函数

除了构造函数,C++中还有一种用于初始化的成员函数,即拷贝构造函数。与构造函数一样,当程序员不定义拷贝构造函数时,编译器也会提供一个拷贝构造函数。以Calculate类为例,其拷贝构造函数的形式如下;

Calculate:: Calculate (const Calculate &c)

{

       m_a = c.m_a;

       m_b = c.m_b;

}

可以看到拷贝构造函数是有参数列表的,用参数列表中的对象的数据成员对对象进行拷贝初始化。

7. this 指针

在很多时候,成员函数需要对两个对象进行操作,比如比较两个对象。以Calculate类为例,比较两个对象时,函数如下;

Calculate::comp(Calculate &val) const;

比较对象A与B;调用的方式可以是;

A.comp(B);

但是在comp()函数中对象A并没有别名,无法被作为返回值进行返回。C++为了解决这个问题,使用了一个特殊的指针this指针,this指针指向用于调用成员函数的对象,或者说是this是对象的地址。那么,函数可以写成如下形式:

Calculate::comp(Calculate  &val) const

{

       if (val.m_a >= m_a)

       {

              return *this;     //返回this指针的解引用

       }

       else

       {

              return t;

       }

}

那么应该也能理解可能在类成员函数add中看到this->sub()的情况了。

8.使用类

使用类首先要声明类的对象,声明类的对象,就像声明基本类型的变量一样。

Calculate A;

则A成为Calculate的一个对象,该对象可以调用Calculate的公有接口函数了。着和结构体访问成员变量的结构是一样的。

A.add();

如此操作A的返回值为0,因为声明A调用的是默认构造函数(无参数的),可以在调用add之前调用init函数,也可以直接用重用的构造函数:

Calculate A(1, 2);

A.add();//此时返回值为3

也可以使用new来创建对象,这就像结构体,访问成员变量时要用->:

Calculate A = new Caculate;//默认构造函数

A.init(1,2);

A.add();

或:

Calculate A = new Caculate(1,2);//重用构造函数

A.add();

附代码:

//文件Calculate.h
#ifndef CALCULATE_H
#define CALCULATE_H

#include <iostream>
#include <cmath>

class Calculate
{
public:
	Calculate();
	Calculate(int a, int b);
	~Calculate();
	
private:
	int m_a;
	int m_b;
public:
	void init(int a, int b);
	int add();
	int sub(){return (m_a-m_b);}
	int mul();
};

inline int Calculate::mul()
{
	return m_a*m_b;
}
#endif
//Calculate.cpp
#include "Calculate.h"

Calculate::Calculate()
{
	m_a = 0;
	m_b = 0;
}
Calculate::Calculate(int a, int b)
{
	m_a = a;
	m_b = b;
}
void Calculate::init(int a, int b)
{
	m_a = a;
	m_b = b;
}
int Calculate::add()
{
	return m_a+m_b;
}
Calculate::~Calculate()
{
}
//main
#include <iostream>					//预处理器
#include "Calculate.h"
using namespace std; 				//名称空间
int main()							//主函数名
{

	Calculate A;
	A.init(1,2);
	int c = A.add();
	
	Calculate B(1,2);
	int d = B.sub();
}
	

 

  • 13
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

bjtuwayne

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

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

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

打赏作者

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

抵扣说明:

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

余额充值