1.什么是类
类是一种将抽象转换为用户定义类型的C++工具。它将数据表示和操纵数据的方法组合成一个整体。类的基本思想是数据抽象和封装。
类与结构体的区别
class数据成员默认私有
struct数据成员默认公有
类的书写语法规则是:
1.类声明:以数据成员的方式描述数据部分,以成员函数(被称为方法)的方式描述接口函数。类声明用class关键字。
class 类名称(通常类名称的首字母大写)
{
public:
公有成员(外部接口)
private:
私有成员 (只允许本类中的函数访问,而类外部的任何函数都不能访问)
protected:
保护成员(与private类似,差别表现在继承与派生时)
};
private下是类的私有变量和函数,只有类自己的成员函数可以访问;
public下是类的共有变量和函数,可以被外部访问;
protected:私密性介于private与public之间,记住它只是在类的派生类中可以访问即可。后面的笔记会再提到它。
2.类定义:类声明给出了类的框架,而方法定义给出了类的细节。
举个例子
class C
{
private:
int a;
int b;
public:
int aa();
int bb();
};
3.类的方法实现
定义成员函数唯一区别于普通函数的地方就是需要用作用域解析运算符::来关联类名称和函数名,使得可以在函数内用类的私有变量。
int C::aa()
{
return a+b;
}
当然,也可以在类的声明中直接完成成员函数定义。这种情况下,函数默认是一个内联函数。这也要求函数的规模符合内联函数的特点。
class C
{
private:
int a;
int b;
public:
int aa(){return a+b;}
int bb();
int cc();
};
如果不通过类的申明中(只class后的中括号中)定义接口函数来实现内联,可以用inline关键字声明它,但内联函数的实用要求每个文件中都有一个该函数定义,通常就把它写在了头文件中,于是你应该见到过下面的形式(都位于头文件中):
class C
{
private:
int a;
int b;
public:
int aa();
int bb();
int cc();
};
inline int C::aa()
{
return a+b;
}
为了保证数据的安全,避免类的成员函数不修改对象的值,应该尽量将成员函数定义为const成员函数。定义一个的const成员函数时const关键字是放在括号后面,他的意思是这个函数不能修改调用它的对象的值。与返回值是否是常量无关。
int aa() const;//声明于头文件的class声明中
int C:aa() const//定义于源文件中
{
return a+b;
}
4.构造函数
如上述C类中的a和b应该有初始化的操作,当然可以写一个专门的初始化函数,如init()函数来实现a和b的初始化。但是这就意味着每次创建类的对象后都要主动调用一次该init函数来完成初始化工作。实际中存在一种需求就是可以直接在创建类的对象的同时完成某些初始化工作。于是就有了构造函数。
构造函数的名字与类名相同,不含有返回类型,甚至不能有void修饰。按照上述需求,我们声明和定义一个构造函数:
C(int a, int b); //声明于头文件的class声明中
C::Calculate(int a, int b) //定义于源文件中
{
m_a = a;
m_b = a;
}
注意上述写法,为了将构造函数的形参和类的成员变量区分开,成员变量该改为了m_开头,这也是一种编程习惯。
默认构造函数是在未提供初始值时进行对象初始化的构造函数。当一个类没有提供任何的构造函数时,编译器会隐式地定义一个默认构造函数。以C类为例,编译器提供的默认构造函数的形式应该是如下的;
C::C(){};
当然也可以显式的定义默认构造函数。但是,当显式地定义了构造函数后,编译器便不会再提供默认构造函数。而很多时候不能没有默认构造函数,因为经常需要定义一些临时的类对象,当这些对象不带有初始值时,需要默认构造函数进行初始化,所以我们通常需要显式地给出默认构造函数。
C::C() //定义于源文件中
{
m_a = 0;
m_b = 0;
}
我们还可以利用函数重载,实现多个构造函数;可以利用函数参数的默认值来实现某些初识默认值的情况。
5.析构函数
特点:①和类名一样,不过得在前面加上~。
②无参数,无返回值。
③因为无参数,无返回值,所以不可以重载。
④尽量不要自己调用析构函数,但是在某些需要的时候再调用。
用构造函数创建对象之后,程序跟踪该对象直到其过期为止,当对象过期时,程序会自动调用一个特殊的成员函数完成清理工作。这个函数就是析构函数。例如,构造函数使用new来分配内存,那么需要析构函数使用delete释放内存。
析构函数的名称也很特殊:在类名前面加上~ 即可。与构造函数一样,析构函数也没有返回值类型和声明类型,与构造函数不同的是,析构函数没有参数。
~C(); //声明于头文件的class声明中
C::~C() //定义于源文件中
{
}
在析构函数中,不存在类似构造函数中初始化列表的东西来控制成员销毁,析构的过程是隐式的。当程序员不显式地定义析构函数时,编译器会提供一个析构函数,这个析构函数的函数体为空。
大多数情况下,程序员都可以不定义析构函数,而直接使用编译器提供的析构函数,但是当构造函数中使用了动态申请内存的方式为对象的属性进行赋值时,需要程序员自己提供析构函数,因为编译器提供的析构函数不会释放堆内的内存。
一个对象被销毁,就会自动调用析构函数,析构函数销毁成员时,按照成员初始化的顺序逆序销毁。一个类有且只有一个析构函数,这就要求构造函数对于动态成员申请内存的方式是一致的。需要记住的是,析构函数的工作是清理对象,而不是删除对象。
- this 指针
this指针是成员函数隐含指针形参,是编译器自己处理的,我们不能在成员函数的形参中添加this指针的参数定义,也不能在调用时显示传递对象的地址给this指针。
编译器会对成员函数进行处理,在对象调用成员函数时,对象地址作实参传递给成员函数的第一个形参this指针。’
在很多时候,成员函数需要对两个对象进行操作,比如比较两个对象。以C类为例,比较两个对象时,函数如下;
C::comp(C &val) const;
比较对象A与B;调用的方式可以是;
A.comp(B);
但是在comp()函数中对象A并没有别名,无法被作为返回值进行返回。C++为了解决这个问题,使用了一个特殊的指针this指针,this指针指向用于调用成员函数的对象,或者说是this是对象的地址。那么,函数可以写成如下形式:
C::comp(C &val) const
{
if (val.m_a >= m_a)
{
return *this; //返回this指针的解引用
}
else
{
return t;
}
}
那么应该也能理解可能在类成员函数aa中看到this->bb()的情况了。