c++类中六个默认成员函数

一.类中六个默认成员函数简介
六个默认成员函数是指,构造函数,拷贝构造函数,赋值运算符重载函数,析构函数,取地址操作符重载、const

修饰的取地址操作符重载。

1.构造函数

特点:(1)函数名与类名相同

   (2)可以重载

   (3)构造对象时自动调用构造函数

   (4)无返回值

   (5)如果类中没有给出构造函数,编译器会自动产生一个缺省的构造函数,如果类中有构
    造函数,编译器就不会产生缺省构造函数。

   (6)全缺省的构造函数和无参的构造函数只能有一个,否则调用的时候就会产生冲突。(想想缺省参    数的使用就明白了)

   (7)构造函数就是给类的对象的成员变量进行初始化,初始化的方法也有两种

     列表初始化,和函数内初始化(下面有解释)

例:(使用日期类为例)无参构造函数

class Date
{
	int _year;
	int _month;
	int _day;
public:
	Date()	//可以理解为变相的全缺省  定义对象时方法和全缺省相同
	{
		cout<<"Date(int year,int month,int day)"<<endl;
		this->_year = 5;
		this->_month = 6;
		this->_day = 7;
	}
}
 缺省构造函数(可以全缺省,也可以部分缺省(遵循缺省规则))

class Date
{
	int _year;
	int _month;
	int _day;
public:
	Date(int year ,int month = 2, int day =1)	//遵循缺省规则
	{
		cout<<"Date(int year,int month,int day)"<<endl;
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}
}
初始化列表初始化成员变量

构造函数的执行可以分成两个阶段,初始化阶段和计算阶段,初始化阶段先于计算阶段。
初始化阶段
所有类类型(class type)的成员都会在初始化阶段初始化
,即使该成员没有出现在构造函数的初始化列表中。
 tip(初始化列表初始化时,对象正在创建,没有this指针) 

计算阶段:一般用于执行构造函数体内的赋值操作 ,如果是内部数据类型赋值(如int,char),

则效率和初始化列表初始化效率差不多,如果是定义的类 类型,可能会涉及到多次调用

所以从这方面考虑,初始化列表的效率相对于构造函数内部赋值要高。

class Date
{
	int _year;
	int _month;
	int _day;
public:
	Date(int year = 1900 ,int month = 1, int day =1)
		:_year(year)
		,_month(month)
		,_day(day)
	{
	}
}
成员列表的效率问题

class Time{  
public:  
    Time(int hour= 1, int minute=1, int second=1)  
    {  
        m_hour = hour;  
        m_minute = minute;  
        m_second = second;  
        cout << "构造时间类对象中..." << endl;  //方便一会测试观察
    }  
private:  
    int m_hour;  
    int m_minute;  
    int m_second;  
};  
class Date  
{  
public:  
    Date(int year, int month, int day,Time t)  
	//:m_year(year),  
  //      m_month(month), 
  //      m_day(day),
	//m_t(t)
          
    {  
        m_year = year;  
        m_month = month;  
        m_day = day;  
        m_t = t;  
    }  
    void print()  
    {  
        cout << m_year << "-" << m_month << "-" << m_day << endl;  
    }  
private:  
    int m_year;  
    int m_month;  
    int m_day;  
    Time m_t;  
};  
int main()  
{  
    Time t(10,36,20);  
    Date d(2017,6,26,t); 
	d.print();
    system("pause");  
    return 0;  
}

如果使用初始化列表 给m_t初始化,只在main函数中定义对象t时调用一次Time的构造函数。

而在构造函数体内初始化则调用两次Time的构造函数,一次在main中,一次在Date的构造函数中创建m_t调用

测试:

这是使用初始化列表

这是在构造函数内部赋值。

有些成员变量必须再初始化列表中初始化,比如:
1. 常量成员变量。(常量创建时必须初始化,因为对于一个常量,我们给它赋值,是不
对的)

2. 引用类型成员变量。(引用创建时必须初始化)
3. 没有全缺省构造函数的类成员变量。(如果构造函数的参数列表中有一个类的对象,并
且该对象的类里没有缺省参数的构造函数时,要是不使用初始化列表,参数中会调用无
参或者全缺省的构造函数,而那个类中又没有。)比如创建m_t时,没有给m_t实参,如

果Time类中没有全缺省构造参数或者默认构造参数,则m_t无法在构造函数体内创建,

系统就会提示,没有默认的构造函数

———————————————————————————————————————————————————

2.拷贝构造函数
构造一个对象d1,并拷贝另一个对象d(此时构造出的d1并不调用构造函数,而是直接调用拷贝构造函数)

函数写法和使用方法(以Date类为例)

为什么传参使用传引用的方法 ?

仔细想想,如果不传引用,则形参就会调用拷贝构造来拷贝实参,在形参调用拷贝构造函数时,形参的形参又会调用拷贝构造,这样下去会陷入无限的递归。

	Date(const Date& d)	//函数写法
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
		cout<<"拷贝"<<endl;
	}
int main()	//使用方法
{
	Date d(12,3,4);
	Date d1(d);
	return 0;
}

如果用默认的拷贝构造函数,会在一些时候出现问题,比如使用动态内存开辟空间时。用通讯录的联系人创建为例

public:
	Contact(char* name, int age)
	{
		m_name = (char *)malloc(sizeof(char)*10);  
        if (NULL == m_name)  
        {  
            cout << "out of memory" << endl;  
            exit(1);  
        }  
        strcpy(m_name,name);  
        m_age = age;
	}
	~Contact()  
   	 {  
        	free(m_name);  
        	m_name = NULL;  
  	  }
int main()
{
	Contact c(chen,12);
	Contact c1(c);
	return 0;
}
当c创建后,c.m_name指向堆上的一个空间,c1对c拷贝构造,拷贝构造是传引用,所以c1.m_name指向是堆上c1.m_name所指向的空间,当c和c1生命周期结束时,会调用析构函数(释放内存,详情看下面讲解),而同一块空间

被释放两次,系统就会报错。

如果是值的拷贝,则为浅拷贝,相对的深拷贝是为对象重新分配一块空间后,再拷贝。

深浅拷贝在之后再详细讨论。

调用拷贝构造函数的3种情况:
1.当用类的一个对象去初始化该类的另一个对象时。Date d1(d)
2.当函数的形参是类的对象,调用函数时进行形参和实参的结合时。比如刚刚讲的Date d(2017,6,26,t);形参临时拷贝 
3.当函数的返回值是对象,函数执行完返回调用者时。(函数运行结束后,返回的对象
会复制到一个无名对象中,然后返回的对象会消失,当调用语句执行完之后,无名对
象就消失了)
调用拷贝构造函数的两种方法:
1.代入法:
Contact c1(c);
2.赋值法:
Contact
 c1 = c;

____________________________________________________________________________________________

3.析构函数

定义方法

~Contact()  
   	 {  
        	free(m_name);  
        	m_name = NULL;  
  	  }


析构函数是一种特殊的成员函数,具有以下特点:
1. 析构函数函数名是在类名加上字符~。

2. 无参数无返回值(但有this指针)。

3. 一个类有且只有一个析构函数,所以肯定不能重载。若未显示定义,系统会自动生成
缺省的析构函数。
4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
5. 注意析构函数体内并不是删除对象,而是做一些清理工作。(比如我们在构造函数
中动态开辟过一段空间,函数结束后需要释放,而系统自动生成的析构函数不管内
存释放,所以需要人为地写出析构函数)
注意:对象生命周期结束后,后构造的对象先释放。

———————————————————————————————————————————————————

4.赋值运算符重载函数
赋值运算符重载函数是两个已有对象一个给另一个赋值的过程。它不同于拷贝构造函数,拷贝构造函数是
用已有对象给新生成的对象赋初值的过

类默认的赋值运算符重载函数是浅拷贝

Date& operator=(const Date& d)
	{
		if(this != &d)	//=左右两个对象不为同一个对象再进行赋值操作
		{
			this->_day = d._day;
			this->_month = d._month;
			this->_year = d._year;
		}
		return *this;
	}


如果赋值的对象中存在类似内存开辟的问题,则也要考虑到深拷贝的问题。所以此时不能使用类默认的

赋值运算符重载函数,需要手动写出深拷贝版的赋值运算符重载函数

———————————————————————————————————————————————————

5.const修饰的取地址操作符的重载

const Date * operator&()  const  //这里的const修饰的是默认参数this
	{  
        	return this;  
	} 


____________________________________________________________________________________________

6.取地址操作符的重载

	Date * operator&()    
	{  
        return this;  
	} 


总结:

6个默认函数中,前4个互相联系,在不同情况下调用不同的函数,看似复杂,只要搞清楚它们各自的职责,再回头看Date类的各种函数方法,就豁然开朗了。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值