类(class/struct):数据(成员变量);程序(成员函数)。
访问限定符:public(公有),protected(保护),private(私有)。
特点:
1. public成员可从类外部直接访问,private/protected成员不能从类外部直接访问。
2. 每个限定符在类体中可使用多次,它的作用域是从该限定符出现开始到下一个限定符之前或类体结束前。
3. 类体中如果没有定义限定符,则默认为私有的。
4. 类的访问限定符体现了面向对象的封装性。
作用域:局部域,全局域,类域,名字空间域。
类的作用域特点:
1. 每个类都定义了自己的作用域,类的成员(成员函数/成员变量)都在类的这个作用域内,成员函数内可任意访问成员变量和其它成员函数。
2. 对象可以通过. 直接访问公有成员,指向对象的指针通过->也可以直接访问对象的公有成员。
3. 在类体外定义成员,需要使用:: 作用域解析符指明成员属于哪个类域。
class Person
{
public :
void Display (); //声明
public :
char* _name; // 名字
char* _sex; // 性别
int _age; // 年龄
};
void Person::Display () //定义
{
cout<<<"-"<< _sex<<"-" <<_age<< endl;
}
类实例化出对象
1.类只是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它。
2.一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间存储类成员变量。
类对象存储模型:
1.每个对象的大小为类中所有成员变量的大小之和,当然这里也遵循内存对齐原则(提高效率)。
2.数据在内存中,成员函数保存在公共代码区里。
3.空类对象(无成员变量的类)的大小为一个字节。
结构体内存对其规则:
1. 结构体成员的第一个元素在结构体变量偏移量为0的地址处。
2. 从第二个成员开始成员的地址对其到对齐数(成员自身的大小和默认对其数的最小值)的整数倍处。(VS默认值为8,linux默认值是4)
3. 结构体的总大小是:所有对齐数中最大对齐数的整数倍。
4. 如果有结构体嵌套,嵌套的结构体对其到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
隐含的this指针
1. 每个成员函数都有一个指针形参,它的名字是固定的,称为this指针,this指针是隐式的。(构造函数比较特殊,没有这个隐含this形参)
2. 编译器会对成员函数进行处理,在对象调用成员函数时,对象地址作实参传递给成员函数的第一个形参this指针。
3. this指针是成员函数隐含指针形参,是编译器自己处理的,我们不能在成员函数的形参中添加this指针的参数定义,也不能在调用时显示传递对象的地址给this指针。
void Display()
{
cout<<_year<<endl;
}
//编译器处理成员函数隐含的this指针为
void Display(Date* this)
{
cout<<this->_year<<endl;
}
类中的默认成员函数
构造函数
成员变量为私有的,要对它们进行初始化,必须用一个公有成员函数来进行。同时这个函数应该有且仅在定义对象时自动执行一次,这时调用的函数称为构造函数(constructor)。
构造函数是特殊的成员函数,其特征如下:
1. 函数名与类名相同。
2. 无返回值。
3. 对象构造(对象实例化)时系统自动调用对应的构造函数。
4. 构造函数可以重载。
5. 构造函数可以在类中定义,也可以在类外定义。
6. 如果类定义中没有给出构造函数,则C++编译器自动产生一个缺省的构造函数,但只要我们定义了一个构造函数,系统就不会自动生成缺省的构造函数。
7. 无参的构造函数和全缺省值的构造函数都认为是缺省构造函数,并且缺省的构造函数只能有一个。
无参构造函数与带参构造函数
class Date
{
public:
//无参构造函数
Date()
{}
//带参数构造函数
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
void TeatDate1()
{
Date d1;//调用无参构造函数
Date d2(1900, 1, 1);//调用带参构造函数
Date d3();//有警告,这里没有调用d3的构造函数定义出d3
d1.Display();
d2.Display();
}
带参缺省参数的构造函数
class Date
{
public :
// 缺省参数的构造函数
Date (int year = 2000, int month = 1, int day = 1)
{
_year = year ;
_month = month ;
_day = day ;
}
//半缺省参数的构造函数(不常用)
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 (1900, 1, 1); // 调用缺省构造函数
}
注意:若缺省参数声明和定义分离,则可以在声明或定义中给默认参数。
类的成员变量有两种初始化方式:
1. 初始化列表。初始化列表以一个冒号开始,接着一个逗号分隔数据列表。
2. 构造函数体内进行赋值。每个数据成员都放在一个括号中进行初始化。尽量使用初始化列表进行初始化,因为它更高效。
成员变量必须放在初始化列表里面的有:
1. 常量成员变量。(常量创建时必须初始化)
2. 引用类型成员变量。(引用创建时必须初始化)
3. 没有缺省构造函数的类成员变量。
#include <iostream>
using namespace std;
class Time
{
public:
Time(int hour = 0, int minute = 0, int second = 0)
{
cout<<"Time()"<<endl;
_hour = hour;
_minute = minute;
_second = second;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public :
Date(int year, int month, int day, const Time& t)//该函数调用了Time的构造函数
{
cout<<"Date()-非初始化列表"<<endl;
_year = year;
_month = month;
_day = day;
_t = t;
}
//Date(int year, int month, int day, const Time& t)该函数未调用了Time的构造函数
// :_year(year)
// ,_month(month)
// ,_day(day)
// ,_t(t)
//{
// cout<<"Date()-初始化列表"<<endl;
//}
private :
int _year ;
int _month ;
int _day ;
Time _t;
};
void Test()
{
Time t;
Date d2(1995, 8, 3, t);
}
初始化列表可参看http://www.cnblogs.com/graphics/archive/2010/07/04/1770900.html
成员变量按声明顺序依次初始化,而非初始化列表出现的顺序。测试代码如下
class Date
{
public:
Date(int x, int y)
:_day(x)//3
,_month(y)//2
,_year(x)//1
{
cout<<"Date()"<<endl;
}
void Display()
{
cout<<"year:"<<_year<<endl;
cout<<"month:"<<_month<<endl;
cout<<"day:"<<_day<<endl;
}
private:
int _year;
int _month;
int _day;
};
void Test()
{
Date d1(1, 2);
d1.Display();
}
拷贝构造函数
创建对象时使用同类对象来进行初始化,这时所用的构造函数称为拷贝构造函数(Copy Constructor),拷贝构造函数是特殊的构造函数。
特征:
1. 拷贝构造函数其实是一个构造函数的重载。
2. 拷贝构造函数的参数必须使用引用传参,使用传值方式会引发无穷递归调用(循环调用)。
3. 若未显示定义,系统会默认缺省的拷贝构造函数。缺省的拷贝构造函数会,依次拷贝类成员进行初始化。
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 Test()
{
Date d1 ;
Date d2 (d1); // 调用拷贝构造函数
Date d3 = d1; // 调用拷贝构造函数
}
析构函数
当一个对象的生命周期结束时,C++编译系统会自动调用一个成员函数,这个特殊的成员函数即析构函数(destructor)构造函数是特殊的成员函数,其特征如下:
1. 析构函数在类名加上字符~。
2. 析构函数无参数无返回值。
3. 一个类有且只有一个析构函数。若未显示定义,系统会自动生成缺省的析构函数。
4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
5. 注意析构函数体内并不是删除对象,而是做一些清理工作。
class Date
{
public:
// 析构函数
~Date()
{}
private:
int _year ;
int _month ;
int _day ;
};
class Array
{
public:
Array (int size)
{
_ptr = (int *)malloc(size*sizeof(int));
}
// 这里的析构函数需要完成清理工作(释放内存)。
~ Array ()
{
if (_ptr )
{
free(_ptr );
_ptr = 0;
}
}
private:
int* _ptr;
};
赋值运算符重载函数(为了增强程序的可读性,C++支持运算符重载)
运算符重载特征:
1.operator+合法的运算符构成函数名。
2. 重载运算符以后,不能改变运算符的优先级/结合性/操作数个数。
C++不能重载的5个运算符: .*/ ::/ sizeof/ ?:/ .更详细点这
class Date
{
public :
Date()
{}
// 拷贝构造函数
Date (const Date& d)
: _year(d._year)
, _month(d._month)
, _day(d._day)
{}
// 赋值操作符的重载
Date& operator = (const Date& d)
{
if (this != &d)//考虑到深浅拷贝问题
{
this->_year = d. _year;
this->_month = d. _month;
this->_day = d. _day;
}
return *this ;//*this指向的对象在函数释放时不会销毁,所以返回值可以使用引用提高效率
}
private:
int _year ;
int _month ;
int _day ;
};
const修饰成员函数
在成员函数后面加const,const修饰this指针所指向的对象,也就是保证调用这个const成员函数的对象在函数内不会被改变。
结论:
1.const对象可以调用const成员函数,不能调用非const成员函数。
2.非const对象可也调用const成员函数,也可以调用const成员函数。
3.const成员函数可以调用const成员函数,不能调用非const成员函数。
4.非const成员函数可以调用const成员函数,也可以调用非const成员函数。
(一句话,就是调用时权限可以缩小,不能放大)
取地址运算符
class Date
{
public :
Date* operator &()
{
return this ;
}
const Date * operator&() const
{
return this ;
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
};