1. 类的定义
定义一个简单的类:
class Person
{
public:
void Display()
{
}
public:
char* _name;
char* _sex;
int _age;
};
如图所示:
2. 成员函数的声明和定义
(1)类内定义
class Person
{
public:
void Display()
{
cout << _name << "-" << _sex << "-" << _age << endl;
}
public:
char* _name;
char* _sex;
int _age;
};
(2)类外定义
class Person
{
public:
void Display();
public:
char* _name;
char* _sex;
int _age;
};
void Person::Display()
{
cout << _name << "-" << _sex << "-" << _age << endl;
}
3. 访问限定符
类的访问限定符有三种:
(1)public成员可以从类外直接访问,privata/protected成员不能从类外部直接访问。
(2)每个限定符在类体中可以使用多次,它的作用域是从该限定符出现开始到下一个限定符之前或类体结束前。
(3)类体中如果没有定义限定符,则默认为private(私有)的。
(4)类的访问限定符体现了面向对象的封装性。
4. 类的作用域
类的作用域有四个:
(1)每个类都定义了自己的作用域,类的成员(成员函数/成员变量)都在类的这个作用域内,成员函数内可任意访问成员变量和其它成员函数。
(2)对象可以通过 . 直接访问公有成员,指向对象的指针通过->也可以直接访问对象的公有成员。
(3)在类体外定义成员,需要使用 :: 作用域解析符指明成员属于哪个类域。
5. 类实例化对象
一个类可以实例化出的对象占用实际的物理空间存储类成员变量。如图所示:
注意:
类知识一个模型一样的东西,限定了类的成员,定义出的一个类并没有分配实际的内存空间来存储它。
6. 对象的大小
每个对象的大小是类中所有成员变量的大小之和,且遵循内存对齐原则。
内存对齐原则:
(1)第一个成员在与结构体变量偏移量为0的地址处。
(2)其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值
VS中默认的值为8
gcc中的默认值为4
(3)结构体总大小为最大对齐数(每个成员变量除了第一个成员都有一个对齐数)的整数倍。
(4)如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
7. 四个默认成员函数
(1)构造函数
特征如下:
①函数名与类名相同。
②无返回值。
③对象实例化是系统自动调用对应的构造函数。
④构造函数可以重载。
⑤构造函数可以在类中定义,也可以在类外定义。
⑥如果类没有给出构造函数,则C++编译器自动产生一个缺省的构造函数,但只要我们定义了一个构造函数,系统就将它生成缺省的构造函数。
⑦无参的构造函数和全缺省的构造函数都认为是缺省的构造函数,并且缺省的构造函数只能有一个。
下面给出一个日期类,代码如下:
class Date
{
public :
// 1.无参构造函数
Date()
{
}
// 2.带参构造函数
Date (int year, int month , int day )
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
void TestDate1()
{
Date d1;//调用无参构造函数
Date d2 (2015, 1, 1);//调用带参的构造函数
Date d3();//注意这里没有调用d3的构造函数定义出d3
}
class Date
{
public:
//3.缺省参数的构造函数
Date(int year = 2000, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//4.半缺省参数的构造函数(不常用)
Date(int year, int month = 1)
{
_year = year;
_month = month;
_day = 1;
}
private:
int _year;
int _month;
int _day;
};
void Test()
{
Date d1;// 调用缺省构造函数
Date d2(2015, 1, 9);// 调用缺省构造函数
}
(2)拷贝构造函数
创建对象时使用同类对象来进行初始化。
特征如下:
①拷贝构造函数其实是一个构造函数的重载。
②拷贝构造函数的参数必须使用引用传参,使用传值方式会引发无穷递归调用。
③若未显示定义,系统会默认缺省的拷贝构造函数。缺省的拷贝构造函数会,依次拷贝类成员进行初始化。
class Date
{
public :
Date()
{
}
// 拷贝构造函数
Date (const Date& d)
{
_year = d ._year;
_month = d ._month;
_day = d ._day;
}
private :
int _year ;
int _month ;
int _day ;
};
void TestDate1 ()
{
Date d1 ;
//下面两种用法都是调用拷贝构造函数,是等价的。
Date d2(d1);//调用拷贝函数
Date d3 = d1;//调用拷贝函数
}
(3)析构函数
定义:
当一个对象的生命周期结束时,C++编译系统会自动调用一个成员函数,这个特殊的成员函数即析构函数。
特征如下:
①析构函数在类名加上字符~。
②析构函数无参数无返回值。
③一个类有且只有一个析构函数。若未显示定义,系统会自动生成缺省的析构函数。
④对象生命周期结束时,C++编译系统系统自动调用析构函数。
⑤注意析构函数体内并不是删除对象,而是做一些清理工作(本质是释放空间)。
class Date
{
public:
~Date()//析构函数
{}
private:
int _year;
int _month;
int _day;
};
(4)赋值运算重载
运算符的重载是为了增强程序的可读性。
拷贝构造函数是创建的对象,使用一个已有对象来初始化这个准备创建的对象。
而赋值运算符的重载是对一个已存在的对象进行拷贝复制。
特征如下:
①operator+合法的运算符构成函数名(例如重载<运算符:operator<) 。
②重载运算符以后,不能改变运算符的优先级/结合性/操作数个数 。
class Date
{
public:
Date()
{}
//拷贝构造函数
Date(const Date& d)
:_year(d._year)
, _month(d._month)
, _day(d._day)
{}
//赋值运算符重载
public:
//这里返回值必须是Date&d的返回值,这样方便链式访问
//我们这里这样子给参数列表实际在编译器时
//Date& operator=(Date* this,const Date& d)
//这里的这个this指针是隐含的
Date& operator=(const Date& d)
{
//这里if是为了防止自己给自己赋值
if (this != &d)
{
this->_year = d._year;
this->_month =d._month;
this->_day = d._day;
}
return *this;
}
private:
int _year;
int _month;
int _day;
};
void Test()
{
Date d1;
Date d2 = d1;//调用拷贝构造函数
Date d3;
d3 = d1;//调用赋值运算符的重载
}
8. 隐含的this指针
(1)每个成员函数都有一个指针形参,它的名字是固定的,称this指针,this指针是隐式的(构造函数比较特殊,没有这个隐含this指针)。
(2)编译器会对成员函数进行处理,在对象调用成员函数时,对象地址作实参传递给成员函数的第一个形参this指针。
(3)this指针是成员函数隐含指针形参,是编译器自己处理的,我们不能在成员函数的形参中添加this指针的参数定义,也不能在调用时显示传递对象的地址给this指针。