类中默认的六个函数

类中6个默认的函数

构造函数

作用:初始化对象的内存空间

class Test
{
public:
	Test()
	{}
	Test(char* str,int num)
	{
		name=new char[strlen(str)+1]();
		strcpy_s(name,strlen(str)+1,str);
		ma=num;
	}
private:
	char *name;
	int  ma;
};

如果用户提供任意的构造函数则系统就不提供默认的构造函数,如果用户不提供则由系统提供。构造函数可以重载。构造函数只能由系统调用,不依赖对象调用(因为构造函数的作用就是为了实例化出对象)。

初始化列表

初始化:在初始化列表中才叫初始化(开辟空间并赋值),在函数内部是赋值

class Test
{
public:
	Test(int data = 100) : mb(data), ma(mb){} // 初始化列表
	void Show()
	{
		cout << "ma = " << ma << endl;
		cout << "mb =" << mb << endl;
	}
private:
	int ma;
	int mb;
};

int main()
{
	Test test;
	test.Show();
}

成员变量的初始化顺序只与定义变量时的顺序有关,与初始化列表的顺序无关因此在上面程序中,先初始化ma,后初始化mb,在初始化ma时由于mb还是一个无效值,因此ma最终初始化为无效值,mb为100.

零初始化(零构造)

有时对于栈上的数据在初始化时,我们暂时还不知道要给什么值,但是不给初始值不合适,这时就会用到零初始化。

int a=int();//相当于调用了int内置类型默认构造函数
在类中的应用
typedef int Elemtype;
class Node
{
public:
	Node(const  Elemtype& rhs=Elemtype())
	{
		ma=rhs;
		next=NULL;
	}
private:
	Elemtype ma;
	Node* next;
};
class Clink
{
public:
	Clink():head()
private:
	Node head;
};
int main()
{
	clink cli;
}

分析在进行clink实例化出cli时,要调用cli的构造函数,而在clink的构造函数中初始化列表中要对head进行初始化,在Node构造函数的初始化列表中就必须零初始化,如果没有const Elemtype& rhs=Elemtype()这样的零初始化,就会报错没有合适的构造函数可用。

在模板中的应用

现在,假如你正在编写模板,并且希望模板类型的变量已经用初始值初始化完毕,那么此时会遇到问题,内建类型并不能满足你的需要

template<typename T>
void foo()
{
    T x;//如果T是内建类型,那么x本身是一个不确定值
}

由于这个原因,我们就应该显式地调用内建类型地缺省构造函数,并把缺省值设为0(对于bool类型而言,为false)。如调用int()将获得缺省值0.

于是借助以下代码,我们便可以确定对象已经 执行了适当的初始化,即便是内建类型也是如此:

template<typename T>
void foo()
{
    T x = T();
}

对于类模板,在用某种类型实例化该模板后,为了确认它所有的成员都已经初始化完毕,需要定义一个缺省构造函数,通过一个初始化列表来初始化类模板的成员:

template<typename T>
class MyClass
{
 private:
      T x;
 public:
      MyClass() :x()
      {//确认x已被初始化,内建类型对象也是如此

      }
};
析构函数

作用:释放对象所占用的除栈之外的其他资源

class Test
{
public:
	~Test()
	{
		delete [] name;
		name=NULL;
	}
private:
	int  ma;
	char *name;
};

一个类只能有一个析构函数,用户提供系统就不提供。析构函数不可以重载。析构可以手动调用(会退化成普通的函数调用,一般情况下不建议,会导致同一资源多次释放,有时可用于赋值运算符重载),析构依赖对象调用。

构造函数与析构函数一一对应,并且先构造出的对象后进行析构,后构造的先析构。

拷贝构造函数

作用:用一个已存在的对象来生成相同类型的新对象

系统提供的默认拷贝构造函数

默认的拷贝构造函数逐个复制非静态成员,复制成员的值,也叫做值传递。
系统提供的拷贝构造函数是一个浅拷贝(),一旦成员变量中有指针,那么在对象销毁调用析构函数时,就会导致同一块资源被多次释放,从而释放野指针。因此在成员变量中有指针类型时就必须手动实现深拷贝的拷贝构造函数。

class Test
{
public:

	Test(const Test& rhs)
	{
		name=new char[strlen(rhs.name)+1]();
		strcpy_s(name,strlen(rhs.name)+1,rhs.name);
		ma=rhs.ma;
	}
private:
	char *name;
	int  ma;
};
形参必须用引用

Test(const Test& rhs);//除了少一个对象生成之外,如果不使用引用,那么就会导致循环递归生成对象,从而导致栈溢出。

赋值运算符重载函数
Test& operator=(const Test& rhs)
{
	if(this!=&rhs)
	{
		delete[] name;
		name=new char[strlen(rhs.name)+1]();
		strcpy_s(name,strlen(rhs.name)+1,rhs.name)
		ma=rhs.ma;
	}
	return *this;
}

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

系统提供的默认函数

系统提供的是一个浅拷贝,如果成员中存在指针类型,则就需要考虑是否实现深拷贝。

形参const+引用

使用引用,会减少一个临时对象的生成,也就减少了一次构造和析构的调用。

使用const+引用,防止形参被修改,以及接受隐式生成的临时对象(隐式生成的临时对象是一个常量,只有常引用可以引用常量)而当在显式生成临时对象时也可以使用普通引用。临时对象遇到表达式结束则对象销毁。

Test t1=30; //Test是自定义类型,int是内置类型

内置类型(所有的指针都是内置类型):常量 放在寄存器中
自定义类型 :变量 放在内存中
隐式生成的临时对象 :常量 放在寄存器

Test t1=Test(30);//显式生成临时对象来生成新对象t1;
Test t2=Test(hehe,30);//逗号表达式,以右括号的前一个参数为参数调用拷贝构造函数。

取地址操作符的重载函数
class Test
{
public:

	Test* operator&()
	{
		return this;
	}
private:
	char *name;
	int  ma;
};
const修饰的取地址操作符重载函数
class Test
{
public:
	const Test* operator&() const
	{
		return this;
	}
private:
	char *name;
	int  ma;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值