类和对象详谈

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();
}










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值