c++:类的六个默认成员函数

什么是类?类是拥有相同属性和行为的集合

类中有六个默认的成员函数分别是:

  1. 构造函数
  2. 析构函数
  3. 拷贝构造函数
  4. 赋值操作符重载函数
  5. 取地址操作符重载函数
  6. const修饰的取地址操作符重载函数

接下来对前四个函数进行具体的分析

我们先定义一个空类:

class A
{
};

在经过编译器处理之后它就不在为空,编译器会自动加入一些默认的成员函数,即使在这些函数中什么也不做。编译器处理之后的类相当于:

class A
{
public:
    A(); //构造函数
    A(const A& a); //拷贝构造函数
    ~A(); //析构函数
    A& operator =(const A& a); //赋值运算符重载
    A* operator &(); //取址运算符重载
    const A* operator &() const; //取址运算符重载
};

 注意:这些函数在我们没有显式给出时编译器会为我们自动合成。

一、构造函数

1.什么是构造函数?

构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时,由编译器自动调用,在对象的生命周期内只且只调用一次,以保证每个数据成员都有一个合适的初值。作用:初始化对象所占的内存空间。

stdent()
{

}
class Time
{
public:
    //构造函数
    Time(int hour = 0, int minute = 0, int second = 0)
        :_hour(hour),_minute(minute),_second(second)//初始化列表
    {
        doSomeThing...
    }

private:
    int _hour;
    int _minute;
    int _second;
};

2.初始化列表

用于对对象成员进行初始化,格式为在函数名和函数体之间,以一个冒号开始,后面跟着以逗号隔开的数据成员列表,在每个成员后接一个圆括号,括号中为初始化的内容。

3.数据成员初始化顺序

即使初始化列表中的成员顺序与定义顺序不同,初始化顺序实际也与数据成员定义的顺序一致。即先给hour赋值,接着minute,最后second。

 

 

4.总结:

  1. 可以重载,实参决定了调用那个构造函数。(对不同对象赋不同资源)
  2. 新对象被创建,由编译器自动调用,且在对象的生命期内仅调用一次。(不可以手动调动
  3. 如果没有显式定义时,编译器会提供一个默认的构造函数。
  4. 不依赖对象调用(此时对象还没有生成)
  5. 无返回值

类中包含以下成员时必须要在初始化列表中初始化:
(1)引用数据成员:因为引用必须在定义时初始化,且不可重新赋值。
(2)const数据成员:因为它必须初始化,不能赋值。
(3)类类型成员(该类没有缺省的构造函数,有构造函数):因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化。

二、析构函数

析构函数(destructor) 与构造函数相反,当对象脱离其作用域时(例如对象所在的函数已调用完毕),系统自动执行析构函数。

作用:释放对象所占的资源   

~student()
{
    delete [] mname;
    mname = NULL;
}

我们在创建对象时,给对象申请了空间,申请的空间必须手动去释放,所以我们在析构函数中去释放空间。
析构函数在对象生命周期结束前由系统自动调用。 

总结:

  1. 不能重载
  2. 可以手动调用,此时析构函数的调用退化普通函数的调用   
  3. 依赖对象调用

三、拷贝构造函数

1.作用:通过已经存在的对象来创建并初始化新对象。

函数名与类名相同,无返回值,有一个形参(常用const修饰),该参数是本类类型的引用。是构造函数的重载

class Time
{
public:
    //构造函数
    Time(int hour = 0, int minute = 0, int second = 0)
        :_hour(hour)
        ,_minute(minute)
        ,_second(second)
    {
        doSomeThing...
    }

    //拷贝构造函数
    Time(const Time& t)
        :_hour(t._hour)
        ,_minute(t._minute)
        ,_second(t._second)
    {
        doSomeThing...
    }

private:
    int _hour;
    int _minute;
    int _second;
};

2.使用场景

1)使用已经存在的对象创建新的对象

Time t1(12,01,59);
Time t2(t1);12

2)传值方式作为函数的参数

void FunTest1(const Time t)
{}12

3)传值方式作为函数返回值

Time FunTest2()
{
    Time t;
    return t;
}

浅拷贝和深拷贝

系统默认提供的为浅拷贝,类成员变量有指针,实现深拷贝。

像上面这种做法,只是简单的将s赋给 _pStr,即让 _pStr也指向字符串s,这样造成的后果是多个对象指向同一空间,析构(关于析构的概念在下面介绍)出错,这种拷贝方式叫做浅拷贝

class String
{
public:
	String()
	{
		mptr = new char[1]();
	}
	String(char* ptr)
	{
		mptr = new char[strlen(ptr) + 1]();
		strcpy_s(mptr, strlen(ptr) + 1, ptr);
	}
	String(const String& rhs)
	{
		mptr = new char[strlen(rhs.mptr) + 1]();
		strcpy_s(mptr, strlen(rhs.mptr) + 1, rhs.mptr);
	}
	String& operator=(const String& rhs)
	{
		if (this != &rhs)     //自赋值判断
		{
			delete[] mptr;//释放旧资源
			mptr = new char[strlen(rhs.mptr) + 1]();//开辟新资源
			strcpy_s(mptr, strlen(rhs.mptr) + 1, rhs.mptr);//赋值
		}
		return *this;
	}
	~String()
	{
		delete[] mptr;
		mptr = NULL;
	}
private:
	char* mptr;
};
int main()
{
	String str1("hello");
	String str2(str1);
	str1 = str2;
	return 0;
}

如上实现了深拷贝.像这样,给新创建的对象开辟一块独立的空间,再将旧对象的内容拷贝过来,这样就不会发生如上的错误了,这种拷贝方式叫作深拷贝。图解如下。

总结:

  1. 系统默认合成的为浅拷贝,在大多数情况下,我们应该自己写出拷贝构造函数
  2. 它的参数必须使用同类型对象的引用传递。因为对象以值传递的方式进入函数体就会调用拷贝构造函数,这样就会形成无限递归。最终导致栈溢出.

四、赋值操作符(=)重载函数

1.作用:用已存在的对象给相同类型的已存在对象赋值 

String& operator=(const String& rhs)

注意:此处的&引用可去除,不过或多生成一个临时对象,不建议。

普通类型之间的赋值通过简单的=完成

int a = 10;
int b = 20;
a = b;

对于类类型的对象我们需要对‘=’重载,以完成类类型对象之间的赋值。

默认的赋值运算符的重载函数为浅拷贝。

2.深拷贝的实现过程(参考上面的实现过程)

  1. 自赋值判断(防止越界访问)
  2. 释放旧的资源
  3. 生成新的资源
  4. 赋值

5、取址(&)运算符重载

String* operator&()
{
    return this;
}

取址操作符重载函数返回值为该类型的指针,无参数。

6. const修饰的取址运算符重载

const String* operator&() const
{
    return this;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值