this指针:对一个实例来说,可以看到它的成员函数和成员变量,但实例本身不可见,this指针时时刻刻指向这个实例。
class student
{
public:
void Initstudent(char* name,int age,char* gender)
{
strcpy(_name,name);
_age = age;
strcpy(_gender,gender);
}
private:
char _name[20];
int _age;
char _gender[3];
};
void FunTest()
{
cout<<sizeof(student)<<endl;
}
说明:
(1)this指针的类型:类类型 * const,以此为例,student * const;
(2)this指针不影响sizeof的结果,不是对象本身的一部分;
(3)this指针是类成员函数的第一个默认隐含参数,由系统编译器自动维护传递,不可显式传递;
(4)this指针的作用域在类的非静态成员函数内部;
关于类的成员函数的 _thiscall调用约定:
(1)_thiscall仅能够用于类的成员函数上,且参数从右向左压栈;
(2)若类的成员函数参数个数确定,this指针通过ecx传递给被调用者;
(3)若类的成员函数参数个数不确定,则this指针在所有参数被压栈后压入堆栈且调用者清理堆栈,否则函数自己清理堆栈;
那么,为什么叫this指针,而不是引用呢?
this指针的类型为 类类型 * const,它是一个常指针,它的指向保证不会改变;而引用的实质也是利用指针来进行操作。
this指针能否为NULL呢?
class A
{
public:
void FunTest1()
{}
};
void FunTest()
{
A* pt=NULL;
pt->FunTest1();
}
经过验证,this指针可以为NULL;但原因还是有点迷茫。
类的默认成员函数
构造函数:特殊的成员函数,函数名与类名相同,创建类类型对象时,由编译器自动调用,在对象的生命周期内有且仅调用一次,以保证每一个数据成员有一个合适的初始值;
特征:
(1)构造函数没有返回值类型,注意:没有返回值类型与返回值类型为void是两回事;
(2)一个类可以有多个构造函数(包括一个拷贝构造函数,其余称为普通构造函数);
(3)构造函数有初始化列表(可以不使用);
(4)创建类类型对象时,由编译器自动调用,在对象的生命周期内有且仅调用一次;若没有显式定义构造函数,编译器会提供一个默认的构造函数;
(5)构造函数可以重载,实参决定了调用哪个构造函数;
class A
{
public:
A()
{cout<<"A()"<<endl;}
A(const int year,const int month,const int date)
{ _year = year;
_month = month;
_date = date;
cout<<_year<<"-"<<_month<<"-"<<_date<<endl;
}
private:
int _year;
int _month;
int _date;
};
(6)无参构造函数和带有缺省值的构造函数都认为是缺省构造函数,且缺省构造函数只能有一个;
class A
{
public:
/*A()
{}*/
A(const int year=1,const int month=2,const int date=3)
{
_year = year;
_month = month;
_date = date;
}
private:
int _year;
int _month;
int _date;
};
构造函数的初始化列表:位于函数参数表之后,却在函数体{}之前,说明该表里的初始化工作发生在函数体内的任何代码被执行之前;
形如:
初始化顺序:
(1)每个成员在初始化列表中只能出现一次(因为数据成员的初始化仅有一次);
(2)数据成员在类中的定义顺序就是在初始化列表中的初始化顺序,注意:初始化列表仅能初始化数据成员;
class A
{public:
A()
{cout<<"A()"<<endl;}
A(const int year,const int month,const int date)
:_year(year)
,_month(month)
,_date(date)
{
cout<<_year<<"-"<<_month<<"-"<<_date<<endl;
}
private:
int _year;
int _month;
int _date;
};
(3)避免使用数据成员初始化数据成员,以免引来不必要的麻烦;
类中哪些成员必须要放在初始化列表中初始化?
class Time
{
public:
Time(const int hour,const int minute,const int second )
:_hour(hour)
,_minute(minute)
,_second(second)
{}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
Date()
:a(0)
,t(2017,2,28)
{}
private:
const int a;
Time t;
};
类中的引用数据成员、const数据成员、类类型成员(该类没有缺省的构造函数)都必须在初始化列表中初始化;
默认构造函数:若类没有显式定义构造函数时,编译器会自动合成一个构造函数,该构造函数中什么都不做;
那么,什么时候编译器会自动合成构造函数呢?
构造函数的作用:创建对象、初始化对象和类型转换(用explicit修饰构造函数,抑制由构造函数定义的隐式转换);
拷贝构造函数:特殊的构造函数,只有单个形参,且参数是本类类型对象的引用(常用const修饰),创建对象时使用已存在的同类对象来进行初始化,由编译器自动调用。
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_date = d._date;
}
特点:(1)是构造函数的重载;(2)参数必须使用同类型对象的引用传递;
(3)若没有显式定义,系统会自动合成默认的拷贝构造函数,同时,会依次拷贝类的数据成员完成初始化;
拷贝构造函数的使用场景:
(1)对象实例化对象
Date d1(2017,2,28);
Date d2(d1);
(2)将拷贝构造函数的返回值作为函数的参数
void FunTest(const Date d)
{}
(3)传值方式作为函数的返回值
Date FunTest()
{
Date d;
return d;
}
析构函数:析构函数名与类名相同,函数名前加“~”,功能与构造函数功能相反,在对象被销毁时,由编译器自动调用,完成类的一些资源清理工作;
特点:(1)析构函数没有返回值类型,也没有参数;
(2)一个类中必须有且只能有一个析构函数,若没有显式定义析构函数,编译器会自动合成缺省的析构函数;
(3)析构函数的本质是做一些清理工作,并不是删除对象,且在对象生命周期结束时,编译器会自动调用析构函数;
class Carray
{public:
Carray(int cap) //构造函数
:_cap(cap)
{
_pt = (int*)malloc(cap*sizeof(int));
_size = 0;
}
~Carray() //析构函数
{ if(NULL != _pt)
{
free(_pt);
_pt = NULL;
}
_size = 0;
_cap = 0;
}
private:
int *_pt;
int _size;
int _cap;
};
拷贝构造函数和赋值运算符重载的重要性
(1)若不主动编译拷贝构造函数和赋值运算符重载函数,编译器将以“位拷贝”的方式自动生成缺省的函数;若类中含有指针变量,则这两个缺省的函数就隐含了错误;
以类String的两个对象a,b为例,假设a.m_data的内容为“hello”,b.m_data的内容为“world”;现将a赋给b,缺省的赋值运算符重载函数的“位拷贝”意味着执行b.m_data=a.m_data,但这将造成三个错误:【1】b.m_data原有的内存没被释放,造成内存泄露;【2】b.m_data和a.m_data指向同一块内存空间,a或b任何一方变动都会影响另一方;【3】在对象被析构时,m_data被释放了两次;
(2)拷贝构造函数与赋值运算符重载函数的区别:拷贝构造函数是在对象被创建时调用的,而赋值运算符重载函数只能被已经存在的对象调用;
类的静态成员:声明为static的类成员;
特点:(1)类的静态成员被所有类对象所共享,不属于某一个具体的实例;
(2)类的静态成员在类中声明,在类外定义(不添加static关键字);
(3)类静态成员的访问形式:类名::静态成员或对象.静态成员;
(4)类静态成员有public、protected和private三种访问权限,具有返回值、const修饰符等参数;
(5)类静态成员函数没有默认的this指针,非静态成员函数中有默认的this指针(this指针的作用域在非静态成员函数的内部);
说明:类的静态成员函数中没有默认的this指针,所以它无法调用类的静态或非静态成员函数;
类的非静态成员函数可以调用类的静态或非静态成员函数,因为不需要this指针;
const关键字
在c中,const int a=10;a是不被修改的变量,称为常变量,可通过数组验证;
在c++中,const int a=10;int arr[a]可通过,表明a为常量;
const int a=10; //a为常量
int* pa = (int*)&a;
*pa = 100;
cout<<a<<endl; //用10将a替换,打印10
cout<<*pa<<endl;//打印100
const修饰类成员
使用场景:(1)const修饰形参,一般和引用同时使用且修饰返回值,同时,在构造函数的初始化列表中修饰类数据成员;
(2)const修饰类的成员函数,实际修饰隐含的this指针,表明在类中不可以对类中的成员进行修改;
(3)在const修饰的类的成员函数中,若要对类的数据成员进行修改,则在该数据成员定义声明前加mutable关键字;
说明:【1】非静态成员函数中有默认的this指针,可用const修饰;静态成员函数和普通函数中没有默认的this指针,所以不可用const修饰;
【2】const对象可调用const成员函数(实质是const修饰的是this指针,则this指针的类型为const Date* const);
【3】非const对象可以调用const成员函数(this指针的类型为const Date* const )和非const成员函数(this指针的类型为Date* const);
【4】const成员函数(this指针的类型为const Date* const)内可以调用其它的const成员函数(this指针的类型为const Date* const);
【5】非const成员函数(this指针的类型为Date* const)可调用其它的const成员函数(this指针的类型为const Date* const)和非const成员函数(this指针的类型为Date* const);
class A
{
public:
void FunTest1()const
{}
void FunTest2()
{}
void FunTest3()const
{
FunTest1();
}
void FunTest4()
{
FunTest1();
FunTest2();
}
};
void FunTest()
{
const A a1;
a1.FunTest1();
A a2;
a2.FunTest3();
a2.FunTest4();
}