一、类
1.类是什么:
类是一种复杂的数据类型,它是将不同类型的数据和与这些数据相关的操作封装在一起的集合体。这有点像C语言中的结构,唯一不同的就是结构没有定义所说的“数据相关的操作”,“数据相关的操作”就是我们平常经常看到的“方法”,因此,类具有更高的抽象性,类中的数据具有隐藏性,类还具有封装性。
例如:
#include<iostream>
using namespace std;
class Time
{
public:
void set_time();
void show_time();
private:
int hour;
int minute;
int second;
};
2.类包含六个默认成员函数:
二、类的默认成员函数
1、构造函数:
(1)构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时,由编译器自动调用,在对象的生命周期内只且只调用一次,以保证每个数据成员都有一个合适的初始值。
#include<iostream>
using namespace std;
class Time
{
public:
Time() //构造函数
{
hour = 0; //利用构造函数对对象中的数据成员赋初值
minute = 0;
second = 0;
}
void set_time();
void show_time();
private:
int hour;
int minute;
int second;
};
void Time::set_time()
{
cin >> hour >> minute >> second;
}
void Time::show_time()
{
cout << hour << ":" << minute << ":" << second << endl;
}
int main()
{
Time t1;
//t1.set_time(); //调用赋值函数
//t1.show_time();
t1.show_time(); //不调用赋值函数
system("pause");
return 0;
}
(2)构造函数特性:
a)函数名与类名相同。
b)没有返回值。
c)有初始化列表(可以不用)。
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个数据成员后面跟一个放在圆括号中的初始化式。
class Time
{
public:
Time(int hour, int minute, int second) //构造函数
: _hour(hour)
, _minute(minute)
, _second(second)
{
}
void set_time();
void show_time();
private:
int _hour;
int _minute;
int _second;
};
初始化顺序:
1>每个成员在初始化列表中只能出现一次。
2>初始化列表仅用于初始化数据成员,并不指定这些这些数据成员的初始化顺序,数据成员在类中定义顺序就是在参数列表中的初始化顺序。
3>尽量避免使用成员初始化成员,成员的初始化顺序最好和成员的定义顺序保持一致。
类中必须要放在初始化列表中的成员:
1>引用数据成员
2>const数据成员
3>类类型成员(该类没有缺省的构造函数)
d)新对象被创建,由编译器自动调用,且在对象的生命周期内金调用一次。
e)构造函数可以重载,实参决定了调用哪个构造函数。
#include<iostream>
using namespace std;
class Time
{
public:
Time() //无参构造函数
{
hour = 0; //利用构造函数对对象中的数据成员赋初值
minute = 0;
second = 0;
}
Time(int a, int b, int c) //构造函数重载
{
hour = a;
minute = b;
second = c;
}
void set_time();
void show_time();
private:
int hour;
int minute;
int second;
};
f)如果没有显示定义时,编译器会提供一个默认的构造函数。
g)无参构造函数和带有缺省值的构造函数都认为是缺省构造函数,并且缺省构造函数只能有一个。
h)构造函数不能用const来修饰(构造函数的功能是对数据成员初始化,而const修饰的函数不能对数据进行更改)
(3)默认构造函数:
类如果没有显示定义构造函数时,编译器会合成一个默认的构造函数,该构造函数中什么工作都不做。
(4)构造函数的作用:
1>构建对象
2>初始化对象
3>类型转换
(5)关键字explicit的作用:
用explicit修饰构造函数,抑制由构造函数定义的隐式转换,explicit关键字类内部的构建声明上,在类的定义体外部的定义上不再重复。
2、拷贝构造函数
(1)拷贝构造函数只有单个形参,而且该形参是对本类类型对象的引用(常用const修饰),这样的构造函数成为拷贝构造函数。拷贝构造函数是特殊的构造函数,创建对象时使用已存在的类对象来进行初始化,由编译器自动调用。
class Time
{
public:
Time() //构造函数
{ }
Time(const int a, const int b, const int c) //构造函数重载
{
hour = a;
minute = b;
second = c;
}
Time(const Time &t1) //拷贝构造函数
{
hour = t1.hour;
minute = t1.minute;
second = t1.second;
}
private:
int hour;
int minute;
int second;
};
(2)特征:
a.他是构造函数的重载。
b.它的参数必须使用同类型的对象的引用传递。
c.如果没有显示定义,系统会自动合成一个默认的拷贝构造函数。默认的拷贝构造函数会依次拷贝类的数据成员完成初始化。
(3)使用场景:
a.对象实例化对象
Time t1(12,2,36);
Time t2(t1);
b.传值方式作为函数的参数
void FunTest()
{ }
c.传值方式作为函数返回值
Time FunTest()
{
Time date;
return date;
}
3、析构函数
(1)析构函数:与构造函数功能相反,在对象被销毁时,由编译器自动调用,完成类的一些资源清理和汕尾工作。
class CArray
{
public:
CArray(size_t capacity)
:_capacity(capacity)
{
_pData = (int*)malloc(capacity*sizeof(int));
_size = 0;
}
~CArray()
{
if (NULL != _pData)
{
free(_pData);
_pData = NULL;
}
_size = 0;
_capacity = 0;
}
private:
int *_pData;
size_t _size;
size_t _capacity;
};
(2)特性:
a.析构函数在类名(即构造函数名)加上字符~。
b.析构函数无参数无返回值。
c.一个类有且只有一个析构函数。若未显示定义,系统会自动生成缺省的析构函数。
d.对象生命周期结束时,C++编译系统自动调用析构函数。
e.注意析构函数体内并不是删除对象,而是做一些清理工作。
4、操作符重载
操作符重载的的方法是定义一个重载操作符的函数,使指定的操作符不仅能实现原有的功能,而且能实现在函数中指定的新的功能。在使用被重载的操作符时,系统就自动调用该函数,以实现相应的功能。操作符重载是通过定义函数实现的。操作符重载实质上是函数的重载。
重载操作符的函数一般格式如下:
函数类型 operator 操作符名称(形参表)
{对操作符的重载处理}
例如,想将“+”用于Complex类(复数)的加法运算,函数的原型为:
Complex operator+(Complex&c1,Complex&c2);
例:
class Complex
{
public:
Complex()
{
_real = 0;
_image = 0;
}
Complex(double r, double i)
{
_real = r;
_image = i;
}
Complex& operator=(const Complex& c);
Complex operator+(const Complex& c);
Complex operator*(const Complex& c);
void display();
private:
double _real;
double _image;
};
Complex& Complex::operator=(const Complex& c)
{
this->_real = c._real;
this->_image = c._image;
return Complex(_real, _image);
}
Complex Complex::operator+(const Complex &c) //有一个默认隐含参数
{
Complex d;
d._real = _real + c._real;
d._image = _image + c._image;
return d;
}
Complex Complex::operator*(const Complex&c)
{
return Complex((_real*c._real - _image*c._image), (_image*c._real + _real*c._image));
}
int main()
{
Complex c1(3, 4), c2(5, -10), c3;
c3 = c1 + c2;
cout << "c1=";
c1.display();
cout << "c2=";
c2.display();
cout << "c1+c2=";
c3.display();
system("pause");
return 0;
}
注:
三、关键字 static、const、extern 的用法
1.关键字static
声明为static的类成员(成员数据或成员函数)称为类的静态成员。
特性:
(1)静态成员为所有类所共享,不属于某个具体的实例。
(2)类静态成员即可用类名::静态成员或者对象.静态成员来访问。
(3)类静态成员变量必须在类外定义,定义时不添加static关键字。
(4)类的静态成员函数没有默认的this指针,因此在它里面不能使用任何非静态成员
(5)静态成员和类的普通成员一样,也有public、protected、private 3种访问级别,也可以具有返回值,const修饰符等参数。
class Date
{
public:
Date(int year = 1990, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{
_count++;
}
void Display()
{
cout << GetDateCount() << endl;
cout << _year << " " << _month << " " << _day << endl;
}
~Date()
{
_count--;
}
static int GetDateCount()
{
Display();
return _count;
}
private:
int _year;
int _month;
int _day;
static int _count;
};
int Date::_count = 0;
2.关键字const
(1)const使用场景:
a、const修饰形参,一般和引用同时使用
b、const修饰返回值
c、const修饰类数据成员,必须在构造函数的初始化列表中初始化
d、const修饰类成员函数,实际修饰隐含的this,表示在类中不可以对类的任何成员进行修改
f 、在const修饰的成员函数中要对类的某个数据成员进行修改,该数据成员定义声明是必须加mutable关键字
(2)举例:
1>const int a;int const a;这两个写法是相等的,表示a是一个int常量
2>const int *a; 表示a是一个指针,可以任意指向int常量或者int变量,它总是把它所指向的目标当作一个int常量。
3>int * const a;表示a是一个指针常量,初始化的时候固定指向一个int变量,之后就不能再指向别的地方了。
4>int const * const;表示a是一个指针常量,初始化的时候必须固定指向一个int常量或者int变量,之后就不能再指向别的地方了,
它总是把它所指向的目标当作一个int常量。也可以写成const int * const a;含义相同。
3.关键字extern
(1)基本解释:
extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。
此外extern也可用来进行链接指定。
(2)extern “C”
C++语言在编译的时候为了解决函数的多态问题,会将函数名和参数联合起来生成一个中间的函数名称,而C语言则不会,
因此会造成链接时找不到对应函数的情况,此时C函数就需要用extern “C”进行链接指定,这告诉编译器,请保持我的名称,
不要给我生成用于链接的中间函数名。
int Date::_count = 0;
#ifdef __cplusplus
#if __cplusplus
extern "C"{
#endif
#endif /* __cplusplus */
…
…
//.h文件结束的地方
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */
a.如果函数的声明中带有关键字extern,仅仅是暗示这个函数可能在别的源文件里定义,没有其它作用。
即下述两个函数声明没有明显的区别:
extern int f(); 和int f();
b.当然,这样的用处还是有的,就是在程序中取代include “*.h”来声明函数,在一些复杂的项目中,
我比较习惯在所有的函数声明前添加extern修饰。关于这样做的原因和利弊可见下面的这个例子:
“用extern修饰的全局变量”
1>在test1.h中有下列声明:
#ifndef TEST1H
#define TEST1H
extern char g_str[]; // 声明全局变量g_str
void fun1();
#endif
2>在test.cpp中
#include "test1.h"
char g_str[] = "123456"; // 定义全局变量g_str
void fun1() { cout << g_str << endl; }