类和对象详谈

来源:互联网 发布:查错别字的软件 编辑:程序博客网 时间:2024/06/06 05:46

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替换,打印10cout<<*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();}










0 0
原创粉丝点击