详解c++中类的六个默认的成员函数(一)

类的6个默认的成员函数包括:

构造函数、析构函数、拷贝构造函数、赋值运算符重载函数、取地址操作符重载、const

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

这篇文章重点解释前四个。

(一)构造函数

构造函数,顾名思义,为对象分配空间,进行初始化。它是一种特殊的成员函数,具有

以下特点:

1.函数名与类名相同。

2.无返回值。

3.构造对象的时候系统会自动调用构造函数。

4.可以重载。

5.可以在类中定义,也可以在类外定义。

6.如果类中没有给出构造函数,编译器会自动产生一个缺省的构造函数,如果类中有构

造函数,编译器就不会产生缺省构造函数。

7.全缺省的构造函数和无参的构造函数只能有一个,否则调用的时候就会产生冲突。

8.没有this指针。因为构造函数才是创建对象的,没有创建对象就不会有对象的首地址。

构造函数,说来就是给成员变量进行初始化。而初始化却有两种方法:

初始化列表、构造函数函数体内赋值。

举例:依然使用日期类来说明:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

class Date
{
public:
	Date()//无参构造函数
	{
		m_year = 2016;
		m_month = 7;
		m_day = 6;
	}
	Date(int year = 1900, int month = 1, int day = 1)//全缺省的构造函数
	{
		m_year = year;
		m_month = month;
		m_day = day;
	}
	Date(int year, int month, int day) :m_year(year), m_month(month), m_day(day)
		//初始化列表初始化成员变量
	{
	}
	void print()
	{
		cout << m_year << "-" << m_month << "-" << m_day << endl;
	}
private:
	int m_year;
	int m_month;
	int m_day;
};
int main()
{
	Date date(2016,7,4);
	date.print();
	system("pause");
	return 0;
}

上边这段代码只是为了解释初始化列表初始化成员变量和在构造函数体内初始化,也解

释了无参构造函数和全缺省的构造函数。声明:由于上边的代码同时给出无参和全缺省

的构造函数,产生调用冲突,编译不通过。

既然有两种初始化的方法,我们究竟该怎样选择呢??

尽量使用初始化列表,因为它更高效。下边用代码说明它是怎么个高效法。

<pre name="code" class="cpp">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_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(2016,7,6,t);
	system("pause");
	return 0;
}

上边给出不使用初始化列表初始化日期类中的时间类对象的 办法,会导致时间类构造两

次,一次在主函数中定义时间类对象时,一次在参数列表中调用。而如果我们将所有

的成员变量都用初始化列表初始化,时间类构造函数只会被调用一次,这就是提高效率

所在。

有些成员变量必须再初始化列表中初始化,比如:

1. 常量成员变量。(常量创建时必须初始化,因为对于一个常量,我们给它赋值,是不

对的)

2. 引用类型成员变量。(引用创建时必须初始化)

3. 没有缺省构造函数的类成员变量。(如果构造函数的参数列表中有一个类的对象,并

且该对象的类里没有缺省参数的构造函数时,要是不使用初始化列表,参数中会调用无

参或者全缺省的构造函数,而那个类中又没有。)

注意:在上边的main函数中要是有这样一句:Date d2();这不是定义一个对象,而是声

明了一个函数名为d2,无参,返回值为Date的函数。

(二)析构函数

析构函数是一种特殊的成员函数,具有以下特点:

1. 析构函数函数名是在类名加上字符~。

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

3. 一个类有且只有一个析构函数,所以肯定不能重载。若未显示定义,系统会自动生成

缺省的析构函数。

4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

5. 注意析构函数体内并不是删除对象,而是做一些清理工作。(比如我们在构造函数

中动态开辟过一段空间,函数结束后需要释放,而系统自动生成的析构函数才不管内

存释放呢,所以需要人为地写出析构函数)

注意:对象生命周期结束后,后构造的对象先释放。

(三)拷贝构造函数:用已有的对象创建一个新的对象。仍然使用上边的日期类举例:

int main()
{
       Date d1(2016,7,6);
       Date d2(d1);
        system("pause");
        return 0;
}

上边是用d1创建一个d2,系统会给出默认的拷贝构造函数,并且该函数的参数是一个常

引用,我们想象为什么必须是引用呢,如果不是又会发生什么。

如果不是引用,形参是实参的一份临时拷贝,由于两者都是对象,此时就会调用自己的

拷贝构造函数,陷入无限递归中.......

上边的代码,我们用默认的拷贝构造函数可以得到正确的结果,有时就不会。实例:

class Person
{
public:
	Person(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;
	}
	~Person()
	{
		free(m_name);
		m_name = 0;
	}
private:
	char *m_name;
	int m_age;
};
int main()
{
	Person p1("yang",20);
	Person p2= p1;
	system("pause");
	return 0;
}

上边的代码会出错,原因见图片。

在析构时,同一块空间释放两次就会有问题。

这种仅仅只是值的拷贝的拷贝方式就是浅拷贝。

深拷贝就是为对象重新分配空间之后,然后将值拷贝的拷贝方式。

下边自己给出拷贝构造函数。

	Person(const Person &p)
	{
		m_name = new char[strlen(p.m_name)+1];
		if (m_name != 0)
		{
			strcpy(m_name,p.m_name);
			m_age = p.m_age;
		}
	}

下边用图给出实现机理。

调用拷贝构造函数的3种情况:

1.当用类的一个对象去初始化该类的另一个对象时。

2.当函数的形参是类的对象,调用函数时进行形参和实参的结合时。

3.当函数的返回值是对象,函数执行完返回调用者时。(函数运行结束后,返回的对象

会复制到一个无名对象中,然后返回的对象会消失,当调用语句执行完之后,无名对

象就消失了)

调用拷贝构造函数的两种方法:

1.代入法:

Person p2(p1);

2.赋值法:

Person p2 = p1;

(四)赋值运算符重载函数

它是两个已有对象一个给另一个赋值的过程。它不同于拷贝构造函数,拷贝构造函数是

用已有对象给新生成的对象赋初值的过程。

默认的赋值运算符重载函数实现的数据成员的逐一赋值的方法是一种浅层拷贝。

浅层拷贝会导致的指针悬挂的问题:

class Person
{
public:
	Person(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;
	}
	~Person()
	{
		free(m_name);
		m_name = 0;
	}
private:
	char *m_name;
	int m_age;
};
int main()
{
	Person p1("yang",20);
	Person p2("yao",20);
	p2 = p1;
	system("pause");
	return 0;
}

看图:

使用深层拷贝来解决指针悬挂的问题:

<pre name="code" class="cpp">Person & operator=(const Person &p)
	{
		if (this == &p)
			return *this;
		delete[]m_name;
		m_name = new char[strlen(p.m_name) + 1];
		strcpy(m_name,p.m_name);
                m_age= p.m_age;
		return *this;
	}

这样先将p2对象里指针指向的旧区域,然后再分配新的空间,再拷贝内容。当然,对于

那些成员变量里没有指针变量就不会涉及到指针悬挂问题。

关于剩下的两个默认成员函数,之后再补充。

时间: 2024-10-10 23:40:21

详解c++中类的六个默认的成员函数(一)的相关文章

免费的HTML5连载来了《HTML5网页开发实例详解》连载(六)媒体查询

响应式设计的另一个重要技术手段是媒体查询.如果只是简单的设计一个流式布局系统,那么可以保证每个网格按比例的放大和缩小,但有可能会使得在小屏幕下(如手机设备)网格太小而严重影响阅读,这样的设计称不上响应式设计.媒体查询可以来解决这一问题.媒体查询可以为特定的浏览器和设备提供特定的样式.媒体查询是CSS 3的一个新特性,是对媒体类型的扩展. 提示:W3C列出了10种媒体类型,请参考http://www.w3.org/TR/CSS2/media.html#media-types. 在响应式设计中,媒体

PHP函数篇详解十进制、二进制、八进制和十六进制转换函数说明

PHP函数篇详解十进制.二进制.八进制和十六进制转换函数说明 作者: 字体:[增加 减小] 类型:转载 中文字符编码研究系列第一期,PHP函数篇详解十进制.二进制.八进制和十六进制互相转换函数说明,主要掌握各进制转换的方法,以应用于实际开发 一,十进制(decimal system)转换函数说明 1,十进制转二进制 decbin() 函数,如下实例 echo decbin(12); //输出 1100 echo decbin(26); //输出 11010 decbin (PHP 3, PHP

MySQL教程详解之存储引擎介绍及默认引擎设置

什么是存储引擎? 与其他数据库例如Oracle 和SQL Server等数据库中只有一种存储引擎不同的是,MySQL有一个被称为“Pluggable Storage Engine Architecture”(可替换存储引擎架构)的特性,也就意味着MySQL数据库提供了多种存储引擎.用户可以根据不同的需求为数据表选择不同的存储引擎,用户也可以根据自己的需要编写自己的存储引擎.MySQL数据库在实际的工作中其实分为了语句分析层和存储引擎层,其中语句分析层就主要负责与客户端完成连接并且事先分析出SQL

EonStor GSe Pro1000详解攻略(六)最全面的数据保护和安全

Infortrend为企业提供全方位数据安全保护,通过多重技术将数据风险降到最低,保证业务不间断的进行.远程复制? 可以远程备份数据,因此当本地数据发生损坏时,可以从异地备份的数据恢复到本地? GSe Pro 1000支持同步备份和排程备份, 实现数据的全方位保护 快照可以根据用户选择的多个时间点快照来恢复数据. SED和文件夹加密硬盘中的数据已加密,即使硬盘被盗,内部数据也不会遭到破解.硬盘内的文件夹也使用AES 256位加密,确保企业数据的安全性. 原文地址:http://blog.51ct

通过ajax前端后台交互/登录页和注册页前端后台交互详解/前端后台交互基础应用/几个后台函数的基础应用/php文件函数基础应用/php字符传函数基础应用/php数组函数基础应用

  前  言  PHP     学习了好久的PHP,今天做一个可以后台交互的登录页和注册页,没做什么判断,简单的了解一下. 具体的内容分析如下: ① PHP中的数据传输-->>由注册页传输给注册页后台-->>注册页后台经过转码保存实例化的文件 ② 在登录页输入账户密码,点击登录时,获得触发函数:获得由后台传输过来的true或者false---转换页面或者弹出输入错误.    登录页后台获取保存账户密码的实例化文件,通过转码,if判断之后传输给前台登录页TURE或者FALSE. 总共

php.ini配置文件详解(为了安全,禁止一些高风险的函数)

配置 disable_function disable_functions= eval,assert,popen,passthru,escapeshellarg,escapeshellcmd,passthru,exec,system,chroot,scandir,chgrp,chown,escapeshellcmd,escapeshellarg,shell_exec,proc_get_status,ini_alter,ini_restore,dl,pfsockopen,openlog,syslo

类中默认的成员函数

1.默认构造函数 2.默认析构函数 3.拷贝构造函数 4.拷贝赋值函数 5.移动构造函数 6.移动拷贝函数

第六周项目四-成员函数、友元函数和一般函数的区别

(1)阅读下面的程序,体会注释中的说明. //例:使用成员函数.友元函数和一般函数的区别 #include <iostream> using namespace std; class Time { public: Time(int h,int m,int s):hour(h),minute(m),sec(s) {} void display1(); //display1是成员函数 friend void display2(Time &); //display2是友元函数 int get

详解关键字static,const,volatile

static static修饰局部变量 static修饰局部变量用于修改变量的存储位置,从自动变量修改为静态变量(在静态区开辟空间,不在栈上开辟),但变量的链接属性和作用域不受影响.static修饰局部变量时该变量在程序执行之前创建,并在程序的整个执行空间一直存在,即使出了作用域也只是被隐藏并不销毁该变量.请看下面的两段代码 代码一:没有static修饰的局部变量 int main() { int i=0; for(i=0;i<10;i++) { int j=10; j+=1; printf(&qu