C++-构造函数,析构函数,初始化列表,执行顺序

记录:构造函数、构造函数的分类、为什么需要构造和析构函数、拷贝构造函数的调用时机、拷贝构造函数的调用时机、对象的初始化列表、组合对象的执行顺序、对象的动态建立和释放

构造函数

有关构造函数
1构造函数定义及调用
1)C++中的类可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数;
2)构造函数在定义时可以有参数;
3)没有任何返回类型的声明。
2构造函数的调用
自动调用:一般情况下C++编译器会自动调用构造函数
手动调用:在一些情况下则需要手工调用构造函数
代码:

class Test
{
public:
	Test()
	{
		cout << "构造函数被调用" << endl;
	}
	~Test()
	{
		cout << "析构函数被调用" << endl;
	}
};

int main()
{
	Test t1;
	Test t2;
	system("pause");
	return 1;
}

构造函数的作用:

class Test
{
public:
	Test()
	{
		a = 10;
		p = (char *)malloc(100);
		strcpy(p, "aaaaffff");
		cout << "构造函数被调用" << endl;
	}
	void pintP()
	{
		cout << p << endl;
		cout << a << endl;
	}
	~Test()
	{
		if (p != NULL)
		{
			free(p);
		}
		cout << "析构函数被调用" << endl;
	}
private:
	int a;
	char *p;
};


int main()
{
	Test t1;
	t1.pintP();

	Test t2;
	t2.pintP();
	system("pause");
	return 1;
}

构造函数的分类

有参数构造、无参构造、赋值构造函数
有参数构造调用有参构造函数有三种方法,括号法 ,等号法(逗号表达式方式),直接调用构造构造函数法

class Test
{
public:
	Test()
	{
		m_a = 0;
		m_b = 0;
		cout << "这是wu参构造函数" << endl;
	}
	Test(int a, int b)
	{
		m_a = a;
		m_b = b;
		cout <<a<<b<< "这是有参构造函数" << endl;
	}
	Test(const Test& obj)
	{
		cout << "我也是构造函数" << endl;
	
	}
private:
	int m_a, m_b;
};
int main()
{
	Test t1(1, 2);   //括号法,
	Test t2 = Test(1, 3);
	system("pause");
	return 1;
}

对象的初始化和对象的赋值
Test t2 = Test(1, 3); //t2对象的初始化,对象的初始化和对象的赋值是两个不同的概念。
**

为什么需要构造和析构函数

1、造函数的调用方法是,隐式调用:自动调用。
代码:

class Test
{
public:
	
	void init(int a, int b)
	{
		m_a = a;
		m_b = b;
		cout << a << b << "这是有参构造函数" << endl;
	}
private:
	int m_a, m_b;
};


int main()
{
	Test t1;
	t1.init(20, 30);
	
	Test tArray[3];  //这种场景下很蹩脚,但是
	tArray[0].init(1, 2);
	tArray[1].init(1, 2);
	tArray[2].init(1, 2);
	//但是如果,tArray【200】在这种场景之下,满足不了需求,所以需要初始化。
	// Test tArray[3];
	system("pause");
	return 1;
}

拷贝构造函数的调用时机

1、第一第二种方法,初始化系统拷贝构造函数。
用对象初始化另一个对象的时候调用拷贝构造函数

class Test
{
public:
	Test(){}
	Test(int a, int b)
	{
		m_a = a;
		m_b = b;
		cout << a << b << "这是有参构造函数" << endl;
	}
	Test(const Test& obj)
	{
		m_a = obj.m_a + 100;
		m_b = obj.m_b + 100;
		cout << "拷贝构造函数" << endl;
	}
private:
	int m_a, m_b;
};
int main()
{
	Test t1(20,30);
	Test t2 = t1;   //默认执行拷贝构造函数。
		Test t1(20,30);
// Test t2 = t1;   //第一种方法:默认执行拷贝构造函数。
Test t3(t1);       // 第二种方法,用t1对象初始化t2对象
	system("pause");
	return 1;
}

拷贝构造函数第三种用法
结论1:若返回的匿名对象,赋值给另外一个同类型的对象,那么匿名对象会被析构
//Location B;
结论2:B = g(); //用匿名对象 赋值 给B对象,然后匿名对象析构
//若返回的匿名对象,来初始化另外一个同类型的对象,那么匿名对象会直接转成新的对象

class Location
{
public:
	Location(int xx = 0, int yy = 0)
	{
		X = xx;  Y = yy;  cout << "Constructor Object.\n";
	}
	Location(const Location & p) 	    //复制构造函数
	{
		X = p.X;  Y = p.Y;   cout << "Copy_constructor called." << endl;
	}
	~Location()
	{
		cout << X << "," << Y << " Object destroyed." << endl;
	}
	int  GetX() { return X; }		int GetY() { return Y; }
private:   int  X, Y;
};

//alt + f8 排版
void f(Location  p)
{
	cout << "Funtion:" << p.GetX() << "," << p.GetY() << endl;
}

void mainobjplay()
{
	Location A(1, 2);  //形参是一个元素,函数调用,会执行实参变量初始化形参变量
	f(A);
}

void main()
{
	mainobjplay();
	system("pause");
}

拷贝构造函数的调用时机*

1)当类中没有定义任何一个构造函数时,c++编译器会提供默认无参构造函数和默认拷贝构造函数
2)当类中定义了拷贝构造函数时,c++编译器不会提供无参数构造函数
3) 当类中定义了任意的非拷贝构造函数(即:当类中提供了有参构造函数或无参构造函数),c++编译器不会提供默认无参构造函数
4 )默认拷贝构造函数成员变量简单赋值
总结:只要你写了构造函数,那么你必须用。

深拷贝和浅拷贝

浅拷贝出现的原因

class Name
{
public:
Name(const char *myp)
{
int len = strlen(myp);
p = (char *)malloc(len + 1);//有斜杠0.
strcpy(p, myp);
}
~Name()
{

	if (p != NULL)
	{
		free(p);
		p = NULL;
		len = 0;
	}

}

private:
char *p;
int len;
};

void displaymain()
{
Name obj1(“22222”);
Name obj2 = obj1; //执行了默认的拷贝函数,执行的是浅拷贝。

}
void main()
{
displaymain();
cout << “Hello” << endl;
system(“pause”);
}

浅拷贝: 只是把栈中对象的值拷贝了一次,并没有在堆中在把内存空间拷贝一份。Name obj2 = obj1; //执行了默认的拷贝函数,执行的是浅拷贝。
在这里插入图片描述
Name obj2 = obj1; //执行了默认的拷贝函数,执行的是浅拷贝。
浅拷贝出现的原因:执行 obj2 = obj1的时候,实在栈中拷贝了一次,然后并没有在堆中开辟新的空间。执行完obi2时候,析构了堆中的地址空间。这时候obj1就出现了‘’野指针‘’情况,当析构obj1时候,就会出现错误。

解决浅拷贝,手动添加一个赋值构造函数变为深拷贝。

**将上面的代码变为深拷贝:**

class Name
{
public:
Name(const char *myp)
{
len = strlen(myp);
p = (char *)malloc(len + 1);//有斜杠0.
strcpy(p, myp);
}
// 手工编写拷贝构造函数,使用深拷贝。Name obj2 = obj1; //执行了默认的拷贝函数,执行的是浅拷贝。
Name(const Name& obj) //手工编写拷贝函数,是深拷贝。
{
len = obj.len;
p = (char *)malloc(len + 1);
strcpy(p, obj.p);
}
~Name()
{

	if (p != NULL)
	{
		free(p);
		p = NULL;
		len = 0;
	}

}

private:
char *p;
int len;
};

void displaymain()
{
Name obj1(“22222”);
Name obj2 = obj1; //执行了默认的拷贝函数,执行的是浅拷贝。

}
void main()
{
displaymain();
cout << “Hello” << endl;
system(“pause”);
}

注:Name obj1(“22222”);
Name obj2 (“”333333“”);
obj2=obj1;//这种情况下也是浅拷贝。只是把指针指在了一起,obj2析构时,obj1又出现了,野指针的情况。

对象的初始化列表

1.必须这样做:
如果我们有一个类成员,它本身是一个类或者是一个结构,而且这个成员它只有一个带参数的构造函数,没有默认构造函数。这时要对这个类成员进行初始化,就必须调用这个类成员的带参数的构造函数,
如果没有初始化列表,那么他将无法完成第一步,就会报错。
实验代码如下:

class A
{
public:
	A(int _a)
	{
		a = _a;
	}
private:
	int a;
};


class B
{
public:
	B(int a, int b)
	{
		b1 = a;
		b2 = b;
	}
private:
	int b1;
	int b2;
	A a1;//初始化A的时候,A没有合适的构造函数,所以分配失败。
	A a2;
};

int main()
{
	A a1(10);
	B objb(10,20);
	cout << "222" << endl;
	system("pause");
	return 1;
}

代码分析:代码执行到A a1时就会报错,原因是因为,B中开辟内存空间的时候,不能调用A中的默认构造函数,这样导致了错误。解决方案就是对象的初始化列表。
代码如下:

class A
{
public:
A(int _a)
{
a = _a;
}
private:
int a;
};
class B
{
public:
B(int a, int b):a1(2),a2(5) //
{
b1 = a;
b2 = b;
}
private:
int b1;
int b2;
A a1;//初始化A的时候,A没有合适的构造函数,所以分配失败。
A a2;
};
int main()
{
A a1(10);
B objb(10,20);
cout << “222” << endl;
system(“pause”);
return 1;
}
如上代码加粗的一行。

如果组合对象有多个,执行顺序如下:

class A
{
public:
	A(int _a)
	{
		a = _a;

	}
	~A()
	{
		cout << "A的析构函数执行" << endl;
	}
private:
	int a;
};
class B
{
public:
	B(int a, int b,int m,int n):a1(m),a2(n)
	{
		b1 = a;
		b2 = b;
	}
	~B()
	{
		cout << "B的析构函数执行" << endl;
	}
private:
	int b1;
	int b2;
	A a1;//初始化A的时候,A没有合适的构造函数,所以分配失败。
	A a2;
};
int main()
{
	// A a1(10);
	B objb(10,20,2,3);
	cout << "222" << endl;
	system("pause");
	return 1;
}

代码执行顺序如下:
先执行A对象a1的构造函数,再执行a2对象的构造,在执行b1的构造函数,
析构函数执行顺序:b1,a2,a1.
**注:**如果类中有const,那么必须被初始化。

强化练习

class ABCD
{
public:
	ABCD(int a,int b,int c)
	{
		this->a = a;
		this->b = b;
		this->c = c;
	}
	~ABCD()
	{
		cout << "~ABCD() construct ," << a << b << c << endl;
	
	}
	int getA()
	{
		return this->a;
	}
private:
	int a;
	int b;
	int c;
};

class MyE
{
public:
	MyE():abcd1(1,2,3),abcd2(1,2,3),m(100)
	{
		cout << "MyE()" << endl;
	}
	~MyE()
	{
		cout << "~MyE()" << endl;
	}
	MyE(const MyE & obj):abcd1(7,8,9),abcd2(10,11,12),m(100)
	{
		cout << "MyE(const MyE & obj)" << endl;
	}
public:
	ABCD abcd1;
	ABCD abcd2;
	const int m;
};
int doThis(MyE mye1)   //会调用Mye类的拷贝构造函数。
{
	cout << "doingthis" << mye1.abcd1.getA() << endl;
	return 1;
}

int run2()
{
	MyE myE;
	doThis(myE);
	return 1;
}
int main()
{
	run2();
	system("pause");
	return 1;
}

输出结果如下:
123
456
MyE()
789
101112
MyE(const MyE & obj)
doingthis7
~MyE()
~ABCD() construct ,101112
~ABCD() construct ,789
~MyE()
~ABCD() construct ,456
~ABCD() construct ,123
请按任意键继续. . .

对象的动态建立和释放

1new和delete基本语法

1)在软件开发过程中,常常需要动态地分配和撤销内存空间,例如对动态链表中结点的插入与删除。在C语言中是利用库函数malloc和free来分配和撤销内存空间的。C++提供了较简便而功能较强的运算符new和delete来取代malloc和free函数。
注意: new和delete是运算符,不是函数,因此执行效率高。
分配基础数据类型:
int *p = new int; //声明基础数据类型。
*p = 20;
cout << *p << endl;
数组的分配和释放:
int *p1 = new int[10];
p1[2] = 111;
delete[] p1;
创建类:

class Test
{
public:
	Test(int a,int b)
	{
		this->a = a;
		this->b = b;
		cout << a << b << endl;
     }
	~Test()
	{
		cout << "使用了构造函数" << endl;
	}
private:
	int a, b;
};

Test *pt = new Test(10, 20);
delete pt;

new和delete操作说明,new可以执行构造函数和delete可以执行构造函数。

new和delete深入分析

问题来源:
用new 开辟的内存free可以释放吗?
用malloc开辟的内存delete可以释放吗?

在基础类型中可以执行,但是在类中会出现下面的情况:

Test *pt = new Test(10, 20);   //会调用类的构造函数
free(pt);//不调用类的析构函数。

Test *p2 = (Test *)malloc(sizeof(Test));   //不会调用类的构造函数。
delete p2;   //会自动调用类的析构函数。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值