C++基础3 类:构造 拷贝 析构函数,

为什么会出现构造函数 与 析构函数

[email protected]:~/c++$ cat main.cpp 
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;

class Test
{
public:
	void init()
	{
		a = 1;
		b = 2;
	}
private:
	int a;
	int b;
};

int main()
{
	Test arr[3] ;
	arr[0].init();	//显式的执行初始化
	arr[1].init();
	arr[2].init();

	Test haha[19999] ;	//请问这个怎么去初始化

	return 0;
}

[email protected]:~/c++$ g++ -g main.cpp  && ./a.out

类的构造函数 与 析构函数

[email protected]:~/c++$ cat main.cpp 
#include <iostream>
using namespace std;
class Test
{
public:
	Test()
	{
		cout << "构造函数 \n";
	}
	~Test()
	{
		cout << "析构函数 \n";
	}
};

int main()
{
	Test t1;
	Test t2;
	cout << "Hello World \n";
	return 0;
}

[email protected]:~/c++$ g++ -g -Wall main.cpp   && ./a.out 
构造函数 
构造函数 
Hello World 
析构函数 
析构函数

构造函数初始化变量:

析构函数释放内存

[email protected]:~/c++$ cat main.cpp 
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
class Test
{
public:
	Test()
	{
		a = 10;
		p = (char *)malloc(100);
		strcpy(p,"Hello Linux!");
		cout << "构造函数 \n";
	}
	void printf_var()
	{
		cout << a << endl;
		cout << p << endl;
	}
	~Test()
	{
		if(p != NULL)
		{
			free(p);
		}
		cout << "析构函数 \n";
	}
private:
	char *p;
	int a;
};
void fun()
{
	Test t1;
	Test t2;
	t1.printf_var();
}

int main()
{
	fun();
	cout << "-------------\n";
	return 0;
}

[email protected]:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
构造函数 
构造函数 
10
Hello Linux!
析构函数 
析构函数 
-------------
[email protected]:~/c++$

类的构造函数3种初始化方式:

[email protected]:~/c++$ cat main.cpp 
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;

class Test
{
public:
	Test()
	{
		cout << "没有默认参数的构造函数 \n";
	}

	Test(int _a)
	{
		a = _a;
		cout << "a=" << a <<"带有1个默认参数的构造函数 \n";
	}

	Test(int _a,int _b)
	{
		a = _a;
		b = _b;
		cout << "a=" << a << "  b=" << b<< "带有2个默认参数的构造函数 \n";
	}

	Test(const Test &OBJ)	//用一个对象初始化另一个对象
	{
		cout << "赋值构造函数构造函数 \n";
	}
	~Test()
	{
		cout << "析构函数 \n";
	}
private:
	int a;
	int b;
};
void fun()
{
	Test t1;	//调用无参数的构造函数

	//===== 	对象的初始化	==========
	/*1*/Test t2(1,2);
	//逗号表达式的最后一个值是整个表达式的值  g++ -Wall 编译有警告:逗号操作符的左操作数没有效果
	/*2*/Test t3 = (3,4,5,6,7);	//这个等号C++编译器做了增强
	/*3*/Test t4 = Test(1,4);/*匿名对象*/
	t1 = t4;	//对象的赋值,这个等号是普通的赋值操作

	Test t5(t2);
}

int main()
{
	fun();
	cout << "-------------\n";
	return 0;
}

正常编译:
[email protected]:~/c++$ g++  -g main.cpp  && ./a.out 
没有默认参数的构造函数 
a=1  b=2带有2个默认参数的构造函数 
a=7带有1个默认参数的构造函数 
a=1  b=4带有2个默认参数的构造函数 
赋值构造函数构造函数 
析构函数 
析构函数 
析构函数 
析构函数 
析构函数 
-------------

g++ -Wall编译
[email protected]:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
main.cpp: In function ‘void fun()’:
main.cpp:44:20: warning: left operand of comma operator has no effect [-Wunused-value]
  /*2*/Test t3 = (3,4,5,6,7);
                    ^
没有默认参数的构造函数 
a=1  b=2带有2个默认参数的构造函数 
a=7带有1个默认参数的构造函数 
a=1  b=4带有2个默认参数的构造函数 
赋值构造函数构造函数 
析构函数 
析构函数 
析构函数 
析构函数 
析构函数 
-------------
[email protected]:~/c++$

【拷贝构造函数应用的4种时机】

1,抛题 -- 乱码的出现:

#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;

class Test
{
public:
	Test(int _a,int _b)
	{
		cout << "2个参数\n";
		a = _a;
		b = _b;
	}
	Test(Test &OBJ)
	{
		cout << "赋值构造函数被调用 \n";
	}
	void fun1()
	{
		cout << "a="<< a << "  ";
		cout << "b="<< b << "  \n";
	}
private:
	int a;
	int b;
};

int main()
{
	Test t1(1,5);	t1.fun1();
	Test t2 = t1;
	//用t1 的参数初始化t2.会调用赋值构造函数,
	//如果没有编译器自己造,如果自定义了赋值构造函数,就直接调用
	t2.fun1();	//此时 输出属性值是乱码。因为我们自定义的构造函数什么也没有做
	return 0;
}

[email protected]:~/c++$ g++ -g main.cpp  && ./a.out 
2个参数
a=1  b=5  
拷贝构造函数被调用 
a=-336889536  b=32764

拷贝构造函数的使用:

第1种调用方法: Test t2 = t1;

[email protected]:~/c++$ cat main.cpp 
//拷贝构造函数应用的4种时机
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;

class Test
{
public:
	Test(int _a,int _b)
	{
		cout << "2个参数\n";
		a = _a;
		b = _b;
	}
	Test(const Test &OBJ)
	{
		a = OBJ.a + 100;
		b = OBJ.a + 150;
		cout << "拷贝构造函数被调用 \n";
	}
	void fun1()
	{
		cout << "a="<< a << "  ";
		cout << "b="<< b << "  \n";
	}
private:
	int a;
	int b;
};

int main()
{
	Test t1(1,5);	t1.fun1();
	Test t2 = t1;
	t2.fun1();
	return 0;
}

chu[email protected]:~/c++$ g++ -g main.cpp  && ./a.out 
2个参数
a=1  b=5  
拷贝构造函数被调用 
a=101  b=151

拷贝构造函数的第2 中调用方式:Test t2(t1);

[email protected]:~/c++$ cat main.cpp 
//拷贝构造函数应用的4种时机
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;

class Test
{
public:
	Test(int _a,int _b)
	{
		cout << "2个参数\n";
		a = _a;
		b = _b;
	}
	Test(const Test &OBJ)
	{
		a = OBJ.a + 100;
		b = OBJ.a + 150;
		cout << "拷贝构造函数被调用 \n";
	}
	void fun1()
	{
		cout << "a="<< a << "  ";
		cout << "b="<< b << "  \n";
	}
private:
	int a;
	int b;
};

int main()
{
	Test t1(1,5);	t1.fun1();
	Test t2(t1);	t2.fun1();
	return 0;
}

[email protected]:~/c++$ g++ -g main.cpp  && ./a.out 
2个参数
a=1  b=5  
拷贝构造函数被调用 
a=101  b=151

【答疑】对象的赋值操作t2 = t1 ,不会调用赋值构造函数:

[email protected]:~/c++$ cat main.cpp 
[email protected]:~/c++$ cat main.cpp 
//拷贝构造函数应用的4种时机
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;

class Test
{
public:
	Test(int _a,int _b)
	{
		cout << "2个参数\n";
		a = _a;
		b = _b;
	}
	Test(const Test &OBJ)
	{
		a = OBJ.a + 100;
		b = OBJ.a + 150;
		cout << "赋值构造函数被调用 \n";
	}
	void fun1()
	{
		cout << "a="<< a << "  ";
		cout << "b="<< b << "  \n";
	}
private:
	int a;
	int b;
};

int main()
{
	Test t1(1,5);	t1.fun1();
	Test t2(99,99);	t2.fun1();
	t2 = t1;	t2.fun1();
	return 0;
}

[email protected]:~/c++$ g++ -g main.cpp  && ./a.out 
2个参数
a=1  b=5  
2个参数
a=99  b=99  
a=1  b=5

赋值构造函数的第3中应用:函数

[email protected]:~/c++$ cat main.cpp 
//拷贝构造函数应用的4种时机
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;

class Test
{
public:
	Test(int _a,int _b)
	{
		cout << "2个参数\n";
		a = _a;
		b = _b;
	}
	Test(const Test &OBJ)
	{
		a = OBJ.a + 100;
		b = OBJ.a + 150;
		cout << "赋值构造函数被调用 \n";
	}
	~Test()
	{
		cout << "a=" << a <<" b =" << b<< "析构函数被调用\n";
	}
	void fun1()
	{
		cout << "a="<< a << "  ";
		cout << "b="<< b << "  \n";
	}
private:
	int a;
	int b;
};
void fun2(Test OBJ)	//会自动调用类的赋值构造函数
{
	OBJ.fun1();
}
void fun1()
{
	Test t1(1,5);	t1.fun1();
	Test t2 = t1;	t2.fun1();
	cout << "t2已经完成初始化\n";
	fun2(t2);	//t2实参会初始化形参OBJ,会调用赋值构造函数
}

int main()
{
	fun1();
	return 0;
}

[email protected]:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
2个参数
a=1  b=5  
赋值构造函数被调用 
a=101  b=151  
t2已经完成初始化
赋值构造函数被调用 
a=201  b=251   
a=201 b =251析构函数被调用
a=101 b =151析构函数被调用
a=1 b =5析构函数被调用

【难点】赋值构造函数应用4

【匿名对象的去和留】-- 去

1,没有对象接收函数的返回

在VS编译下,返回的对象会调用一次析构函数,然后没人接收就析构掉

在GCC下,不会出现这种情况

【这第4种方法 VS环境 与 GCC 表现不一样】

//拷贝构造函数应用的4种时机
#include <iostream>
#include <stdio.h>
using namespace std;

class Test
{
public:
	Test(int _a, int _b)
	{
		a = _a;
		b = _b;
		cout << "a=" << a << " b =" << b << "构造函数初始化, 有2个参数\n";
	}
	Test(const Test &OBJ)
	{
		a = OBJ.a + 1;
		b = OBJ.b + 1;
		cout << "a=" << a << " b =" << b << "赋值构造函数被调用 \n";
	}
	~Test()
	{
		cout << "a=" << a << " b =" << b << "析构函数被调用\n";
	}
	void fun1()
	{
		cout << "a=" << a << "  ";
		cout << "b=" << b << "  \n";
	}
private:
	int a;
	int b;
};

//函数返回一个Test 对象 (复杂类型的)
//返回的是一个新的匿名对象
//所以会调用匿名对象类的copy构造函数
Test fun1()
{
	Test A(66, 77);
	return A;
	//新的对象作为匿名对象返回
}

//用这个函数观测对象的生命周期
void fun2()
{
	//微软VS环境有效:用b对象b来接返回的匿名对象,不会直接析构
	//GCC 既不执行copy构造函数,也不执行析构函数
	fun1();
}

int main()
{
	fun2();
	getchar();
	return 0;
}
GCC编译运行:
[email protected]:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
a=66 b =77构造函数初始化, 有2个参数
a=66 b =77析构函数被调用

VS编译运行:
C:\Users\chunli\Documents\c_c++\ConsoleApplication2\Debug>ConsoleApplication2
a=66 b =77构造函数初始化, 有2个参数
a=67 b =78赋值构造函数被调用
a=66 b =77析构函数被调用
a=67 b =78析构函数被调用

【难点】赋值构造函数应用4

【匿名对象的去和留】-- 留

1,创建一个对象来接收函数的返回

在VS编译下,返回的对象会调用一次析构函数,然后被接收就不会直接析构掉

在GCC下,不会出现这种情况

//拷贝构造函数应用的4种时机
#include <iostream>
#include <stdio.h>
using namespace std;

class Test
{
public:
	Test(int _a, int _b)
	{
		a = _a;
		b = _b;
		cout << "a=" << a << " b =" << b << "构造函数初始化, 有2个参数\n";
	}
	Test(const Test &OBJ)
	{
		a = OBJ.a + 1;
		b = OBJ.b + 1;
		cout << "a=" << a << " b =" << b << "赋值构造函数被调用 \n";
	}
	~Test()
	{
		cout << "a=" << a << " b =" << b << "析构函数被调用\n";
	}
	void fun1()
	{
		cout << "a=" << a << "  ";
		cout << "b=" << b << "  \n";
	}
private:
	int a;
	int b;
};

//函数返回一个Test 对象 (复杂类型的)
//返回的是一个新的匿名对象
//所以会调用匿名对象类的copy构造函数
Test fun1()
{
	Test A(66, 77);
	return A;
	//新的对象作为匿名对象返回
}

//用这个函数观测对象的生命周期
void fun2()
{
	//微软VS环境有效:用b对象b来接返回的匿名对象,不会直接析构
	//GCC 既不执行copy构造函数,也不执行析构函数
	//注意此时是一个新的对象,VS会调用copy构造函数,不会析构
	Test b = fun1();
	b.fun1();
}

int main()
{
	fun2();
	getchar();
	return 0;
}
GCC下编译运行:
[email protected]:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
a=66 b =77构造函数初始化, 有2个参数
a=66  b=77  
a=66 b =77析构函数被调用

VS下编译运行:
C:\Users\chunli\Documents\c_c++\ConsoleApplication2\Debug>ConsoleApplication2
a=66 b =77构造函数初始化, 有2个参数
a=67 b =78赋值构造函数被调用
a=66 b =77析构函数被调用
a=67  b=78
a=67 b =78析构函数被调用

【难点】赋值构造函数应用4

【匿名对象的去和留】-- 去

1,创建一个对象来接收函数的返回

在VS编译下,返回的对象会调用一次析构函数,然后被接收就不会直接析构掉

在GCC下,不会出现这种情况

[email protected]:~/c++$ cat main.cpp 
//拷贝构造函数应用的4种时机
#include <iostream>
#include <stdio.h>
using namespace std;

class Test
{
public:
	Test(int _a, int _b)
	{
		a = _a;
		b = _b;
		cout << "a=" << a << " b =" << b << "构造函数初始化, 有2个参数\n";
	}
	Test(const Test &OBJ)
	{
		a = OBJ.a + 1;
		b = OBJ.b + 1;
		cout << "a=" << a << " b =" << b << "赋值构造函数被调用 \n";
	}
	~Test()
	{
		cout << "a=" << a << " b =" << b << "析构函数被调用\n";
	}
	void fun1()
	{
		cout << "执行对象的成员函数  ";
		cout << "a=" << a << "  ";
		cout << "b=" << b << "  \n";
	}
private:
	int a;
	int b;
};

//函数返回一个Test 对象 (复杂类型的)
//返回的是一个新的匿名对象
//所以会调用匿名对象类的copy构造函数
Test fun1()
{
	cout << "fun1 start \n" ;
	//A  是局部变量,会被析构
	Test A(66, 77);
	return A;
	//新的对象作为匿名对象返回
	cout << "fun1 end \n" ;
}

//用这个函数观测对象的生命周期
void fun2()
{
	//微软VS环境有效:用b对象b来接返回的匿名对象,不会直接析构
	//GCC 既不执行copy构造函数,也不执行析构函数
	cout << "fun2 start \n" ;
	Test b(1,2);
	b.fun1();
	cout << "因为此时匿名对象并没有转换成新对象,匿名对象就会析构\n";
	b =  fun1();
	b.fun1();
	cout << "fun2 end \n" ;
}

int main()
{
	fun2();
	getchar();
	return 0;
}
[email protected]:~/c++$ 
GCC编译运行:
[email protected]:~/c++$ g++ -Wall -g main.cpp  && ./a.out 

fun2 start 
a=1 b =2构造函数初始化, 有2个参数
执行对象的成员函数  a=1  b=2  
因为此时匿名对象并没有转换成新对象,匿名对象就会析构
fun1 start 
a=66 b =77构造函数初始化, 有2个参数
a=66 b =77析构函数被调用
执行对象的成员函数  a=66  b=77  
fun2 end 
a=66 b =77析构函数被调用
[email protected]:~/c++$ 

VS编译运行:
C:\Users\chunli\Documents\c_c++\ConsoleApplication2\Debug>ConsoleApplication2
fun2 start
a=1 b =2构造函数初始化, 有2个参数
执行对象的成员函数  a=1  b=2
因为此时匿名对象并没有转换成新对象,匿名对象就会析构
fun1 start
a=66 b =77构造函数初始化, 有2个参数
a=67 b =78赋值构造函数被调用
a=66 b =77析构函数被调用
a=67 b =78析构函数被调用
执行对象的成员函数  a=67  b=78
fun2 end
a=67 b =78析构函数被调用

结论: 有关 匿名对象的去和留【VS环境】

如果用匿名对象  初始化 另外一个同类型的对象, 匿名对象 转成有名对象

如果用匿名对象  赋值给 另外一个同类型的对象, 匿名对象 被析构

老师的笔记与图:老师的程序:

#include <iostream>
using namespace std;

class Location 
{ 
public:
	Location( int xx = 0 , int yy = 0 ) 
	{ 
		X = xx ;  Y = yy ;  cout << "Constructor Object.\n" ; 
	}

	//copy构造函数  完成对象的初始化
	Location(const Location & obj) //copy构造函数 
	{
		X = obj.X; Y = obj.Y;
	}
	~Location() 
	{ 
		cout << X << "," << Y << " Object destroyed." << endl ; 
	}
	int  GetX () { return X ; }		int GetY () { return Y ; }
private :   int  X , Y ;
} ;

//g函数 返回一个元素 
//结论1 : 函数的返回值是一个元素 (复杂类型的), 返回的是一个新的匿名对象(所以会调用匿名对象类的copy构造函数)

//
//结论2: 有关 匿名对象的去和留
//如果用匿名对象  初始化 另外一个同类型的对象, 匿名对象 转成有名对象
//如果用匿名对象  赋值给 另外一个同类型的对象, 匿名对象 被析构

//
//你这么写代码,设计编译器的大牛们:
//我就给你返回一个新对象(没有名字 匿名对象)
Location g()
{
	Location A(1, 2);
	return A;
}

//
void objplay2()
{
	g(); 
}

//
void objplay3()
{
	//用匿名对象初始化m 此时c++编译器 直接把匿名对转成m;(扶正) 从匿名转成有名字了m
	Location m = g(); 
	printf("匿名对象,被扶正,不会析构掉\n");
	cout<<m.GetX()<<endl;;
}

void objplay4()
{
	//用匿名对象 赋值给 m2后, 匿名对象被析构
	Location m2(1, 2);
	m2 = g();
	printf("因为用匿名对象=给m2, 匿名对象,被析构\n");
	cout<<m2.GetX()<<endl;;
}
void main()
{
	//objplay2();
	//objplay3();
	objplay4();
	cout<<"hello..."<<endl;
	system("pause");
	return ;
}

【回顾上午】

构造函数初始化的三种方式:

[email protected]:~/c++$ cat main.cpp 
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
class Test
{
public:
	Test(int _a)
	{
		a = _a;
		cout << "a=" << a << "构造函数初始化, 有1个参数\n";
	}
	Test(int _a,int _b)
	{
		a = _a;
		b = _b;
		cout << "a=" << a  << "  b= " << b << "构造函数初始化, 有2个参数\n";
	}
	void fun()
	{
		cout << "执行对象的成员函数  ";
		cout << "a=" << a << "  ";
		cout << "b=" << b << "  \n";
	}
private:
	int a;
	int b;
};

int main()
{
	Test t1(1,2);		t1.fun();//C++编译器自动调用构造函数
	Test t2 = (1,5,6);	t2.fun();//C++编译器自动调用构造函数
	Test t3 = Test(4,5);		//程序员手动调用构造函数
	return 0;
}
[email protected]:~/c++$ g++ -g main.cpp  && ./a.out 
a=1  b= 2构造函数初始化, 有2个参数
执行对象的成员函数  a=1  b=2  
a=6构造函数初始化, 有1个参数
执行对象的成员函数  a=6  b=0  
a=4  b= 5构造函数初始化, 有2个参数

【回顾】拷贝构造函数的4种使用时机

GCC编译器 与 VS编译有些区别:

[email protected]:~/c++$ cat main.cpp 
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
int num = 0;
class Test
{
public:
	Test(int _a)
	{
		a = _a;
		cout << "a=" << a << "构造函数初始化, 有1个参数\n";
	}
	Test(int _a,int _b)
	{
		a = _a;
		b = _b;
		cout << "a=" << a  << " b=" << b << "构造函数初始化, 有2个参数\n";
	}
	Test(const Test &OBJ)
	{
		a = OBJ.a + 1;
		b = OBJ.b + 1;
		num++;
		cout << "第"<< num <<"次调用拷贝构造函数";
		cout << "a=" << a << "  b=" << b <<endl ;
	}
	~Test()
	{
		cout << "a=" << a << " b=" << b << "析构函数被调用\n";
	}
	void fun()
	{
		cout << "执行对象的成员函数   ";
		cout << "a=" << a << "  ";
		cout << "b=" << b << "  \n";
	}
private:
	int a;
	int b;
};

void fun1()
{
	cout << "拷贝函数的调用时机,第1种和第2种 \n";
	Test t1(1,2);		t1.fun();
	Test t2 = t1;		t2.fun();	//C++编译器会调用拷贝构造函数
	Test t3(t2) ;		t3.fun();	//C++编译器会调用拷贝构造函数
}
void fun2(Test OBJ)
{
	cout << "拷贝函数的调用时机,第3种\n";
}

Test fun3()	//返回一个Test类
{
	cout << "拷贝函数的调用时机,第4种\n";
	Test t1(7,8);
	return t1;
}

int main()
{
	fun1();	//拷贝函数的调用时机,第1种和第2种
	Test t1(4,5);	fun2(t1);	//拷贝函数的调用时机,第3种
	Test t2 = fun3();		//弄了一个新的对象接受了匿名对象,这个不会直接执行析构函数
	Test t3(11,12);	t3 =  fun3();	//t3不是一个新的对象,匿名类会执行析构函数
	return 0;
}
1 GCC编译执行:
[email protected]:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
拷贝函数的调用时机,第1种和第2种 
a=1 b=2构造函数初始化, 有2个参数
执行对象的成员函数   a=1  b=2  
第1次调用拷贝构造函数a=2  b=3
执行对象的成员函数   a=2  b=3  
第2次调用拷贝构造函数a=3  b=4
执行对象的成员函数   a=3  b=4  
a=3 b=4析构函数被调用
a=2 b=3析构函数被调用
a=1 b=2析构函数被调用
a=4 b=5构造函数初始化, 有2个参数
第3次调用拷贝构造函数a=5  b=6
拷贝函数的调用时机,第3种
a=5 b=6析构函数被调用
拷贝函数的调用时机,第4种
a=7 b=8构造函数初始化, 有2个参数
a=11 b=12构造函数初始化, 有2个参数
拷贝函数的调用时机,第4种
a=7 b=8构造函数初始化, 有2个参数
a=7 b=8析构函数被调用
a=7 b=8析构函数被调用
a=7 b=8析构函数被调用
a=4 b=5析构函数被调用
[email protected]:~/c++$ 

2 VS环境编译执行:
C:\Users\chunli\Documents\c_c++\ConsoleApplication2\Debug>ConsoleApplication2
拷贝函数的调用时机,第1种和第2种
a=1 b=2构造函数初始化, 有2个参数
执行对象的成员函数   a=1  b=2
第1次调用拷贝构造函数a=2  b=3
执行对象的成员函数   a=2  b=3
第2次调用拷贝构造函数a=3  b=4
执行对象的成员函数   a=3  b=4
a=3 b=4析构函数被调用
a=2 b=3析构函数被调用
a=1 b=2析构函数被调用
a=4 b=5构造函数初始化, 有2个参数
第3次调用拷贝构造函数a=5  b=6
拷贝函数的调用时机,第3种
a=5 b=6析构函数被调用
拷贝函数的调用时机,第4种
a=7 b=8构造函数初始化, 有2个参数
第4次调用拷贝构造函数a=8  b=9
a=7 b=8析构函数被调用
a=11 b=12构造函数初始化, 有2个参数
拷贝函数的调用时机,第4种
a=7 b=8构造函数初始化, 有2个参数
第5次调用拷贝构造函数a=8  b=9
a=7 b=8析构函数被调用
a=8 b=9析构函数被调用
a=8 b=9析构函数被调用
a=8 b=9析构函数被调用
a=4 b=5析构函数被调用

【关于默认构造函数】

默认构造函数:

二个特殊的构造函数

1)默认无参构造函数

当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空

2)默认拷贝构造函数

当类中没有定义拷贝构造函数时,编译器默认提供一个默认拷贝构造函数,简单的进行成员变量的值复制

【构造函数调用规则研究 】

1)当类中没有定义任何一个构造函数时,c++编译器会提供默认无参构造函数和默认拷贝构造函数

2)当类中定义了拷贝构造函数时,c++编译器不会提供无参数构造函数

3) 当类中定义了任意的非拷贝构造函数(即:当类中提供了有参构造函数或无参构造函数),c++编译器不会提供默认无参构造函数

4 )默认拷贝构造函数成员变量简单赋值

总结:只要你写了构造函数,那么你必须用。

1,【当你定义了构造函数,你必须使用】:

先演示没有任何构造函数的情景:

[email protected]:~/c++$ cat main.cpp 
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
int num = 0;
class Test
{
public:
	void fun()
	{
		cout << "执行对象的成员函数   ";
		cout << "a=" << a << " b=" << b << "  \n";
	}
private:
	int a;
	int b;
};
int main()
{
	Test t1;	t1.fun();
	return 0;
}

编译运行OK:
[email protected]:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
执行对象的成员函数   a=2071403600 b=32765

当类中定义了构造函数,如果不使用,对象初始化就会报错!

示范:

[email protected]:~/c++$ cat main.cpp 
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
int num = 0;
class Test
{
public:
	Test(int _a,int _b)
	{
		a = _a;
		b = _b;
		cout << "a=" << a  << " b=" << b << "构造函数初始化, 有2个参数\n";
	}
	void fun()
	{
		cout << "执行对象的成员函数   ";
		cout << "a=" << a << " b=" << b << "  \n";
	}
private:
	int a;
	int b;
};
int main()
{
	Test t1;	t1.fun();//定义一个对象
	return 0;
}

编译运行:直接报错
[email protected]:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
main.cpp: In function ‘int main()’:
main.cpp:43:7: error: no matching function for call to ‘Test::Test()’
  Test t1; t1.fun();
       ^

手动定义一个空的构造函数,编译通过!

[email protected]:~/c++$ cat main.cpp 
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
int num = 0;
class Test
{
public:
	Test()
	{

	}
	void fun()
	{
		cout << "执行对象的成员函数   ";
		cout << "a=" << a << " b=" << b << "  \n";
	}
private:
	int a;
	int b;
};
int main()
{
	Test t1;	t1.fun();
	return 0;
}

[email protected]:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
执行对象的成员函数   a=89452272 b=32765  
[email protected]:~/c++$

【结论】

在类的定义时,只要写了构造函数,编译器就不会再自动为你定义无参的构造函数,这个构造函数必须要使用!

构造析构阶段性总结 

1)构造函数是C++中用于初始化对象状态的特殊函数

2)构造函数在对象创建时自动被调用

3)构造函数和普通成员函数都遵循重载规则

4)拷贝构造函数是对象正确初始化的重要保证

5)必要的时候,必须手工编写拷贝构造函数 

【浅拷贝 与 深拷贝】

浅拷贝  示范,程序编译通过,运行就死掉,

因为释放已经释放过的内存,所以死掉

[email protected]:~/c++$ cat main.cpp 
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
int num = 0;
class Test
{
public:
	Test(const char *str)
	{
		cout << "I‘m init....\n";
		len  =  strlen(str);
		p = (char *)malloc(len +1);
		strcpy(p,str);
		*(p + len) = ‘\0‘;

	}
	~Test()
	{
		if(p != NULL)
		{
			free(p);
			p = NULL;
			len = 0;
		}
		cout << "I‘m Free!\n";
	}
	void show()
	{
		cout << p <<"\n";
	}
private:
	char *p;
	int len;
};

void fun1()
{
	Test t1("hello world");	t1.show();
	Test t2(t1);		t2.show();

}
int main()
{
	fun1();
	return 0;
}
编译运行:
[email protected]:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
I‘m init....
hello world
hello world
I‘m Free!
*** Error in `./a.out‘: double free or corruption (fasttop): 0x0000000001a93010 ***
Aborted (core dumped)
[email protected]:~/c++$ 

gdb调试:
[email protected]:~/c++$ gdb ./a.out 
(gdb) run
(gdb) where
#5  0x0000000000400b35 in Test::~Test (this=0x7fffffffead0, __in_chrg=<optimized out>) at main.cpp:23

会看到 at main.cpp:23
这一行就是 free(p);

手动写一个拷贝构造函数吗,完成深拷贝:

[email protected]:~/c++$ cat main.cpp 
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
int num = 0;
class Test
{
public:
	Test(const char *str)
	{
		cout << "I‘m init....\n";
		len  =  strlen(str);
		p = (char *)malloc(len +1);
		strcpy(p,str);
		*(p + len) = ‘\0‘;

	}
	Test(const Test &obj)	// 当外部调用 Test t2(t1);
	{
		//深度拷贝
		len = obj.len;
		p = (char *)malloc(len +1);;
		strcpy(p,obj.p);
		*(p + len) = ‘\0‘;
	}
	~Test()
	{
		if(p != NULL)
		{
			free(p);
			p = NULL;
			len = 0;
		}
		cout << "I‘m Free!\n";
	}
	void show()
	{
		cout << p <<"\n";
	}
private:
	char *p;
	int len;
};

void fun1()
{
	Test t1("hello world");	t1.show();
	Test t2(t1);		t2.show();
        //Test t3 = t1;         =等号也是浅拷贝

}
int main()
{
	fun1();
	return 0;
}
编译运行:
[email protected]:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
I‘m init....
hello world
hello world
I‘m Free!
I‘m Free!

还是会存在宕掉的可能:

Test t1("hello world!"); t1.show();

Test t2("hello Linux!"); t2.show();

t1 = t2; //=等号也是浅拷贝

这样会再次导致一片内存两次free,继续宕机!

[email protected]:~/c++$ cat main.cpp 
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
int num = 0;
class Test
{
public:
	Test(const char *str)
	{
		cout << "I‘m init....\n";
		len  =  strlen(str);
		p = (char *)malloc(len +1);
		strcpy(p,str);
		*(p + len) = ‘\0‘;

	}
	Test(const Test &obj)	// 当外部调用 Test t2(t1);
	{
		//深度拷贝
		len = obj.len;
		p = (char *)malloc(len +1);;
		strcpy(p,obj.p);
		*(p + len) = ‘\0‘;
		cout << "I‘m in copy \n";
	}
	~Test()
	{
		if(p != NULL)
		{
			free(p);
			p = NULL;
			len = 0;
		}
		cout << "I‘m Free!\n";
	}
	void show()
	{
		cout << p <<"\n";
	}
private:
	char *p;
	int len;
};

void fun1()
{
	Test t1("hello world!");	t1.show();
	Test t2("hello Linux!");	t2.show();
	t1 = t2;			//=等号也是浅拷贝

}
int main()
{
	fun1();
	return 0;
}

编译通过,运行出错!
[email protected]:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
I‘m init....
hello world!
I‘m init....
hello Linux!
I‘m Free!
*** Error in `./a.out‘: double free or corruption (fasttop): 0x000000000249b030 ***
Aborted (core dumped)

【gdb调试】:
[email protected]:~/c++$ gdb ./a.out 
(gdb) run
(gdb) where
#5  0x0000000000400b43 in Test::~Test (this=0x7fffffffead0, __in_chrg=<optimized out>) at main.cpp:32
32行就是    free(p);

构造函数 初始化 列表:

1,首先会执行被组合对象的构造函数

2,如果组成对象有多个,安照对象定义的顺序初始化,而不是按照列表的顺序

3,被组合对象的析构顺序与构造顺序相反

[email protected]:~/c++$ cat main.cpp 
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
int num = 0;

class Test1
{
public:
	Test1(int _a)
	{
		a = _a;
		cout << a << " Test1 I‘m init....\n";
	}
	~Test1()
	{
		cout << a << " Test1 I‘m Free!\n";
	}
private:
	int a;
};

class Test2
{
public:
	//在初始化参数的同时,将Test1对象完成初始化
	Test2(int _a):t11(1),t12(2)
	{
		a = _a;
		cout << "Test2  1 I‘m init....\n";
	}
	//经典用法,参数传递
	Test2(int _a,int _b,int _c,int _d):t11(_c),t12(_d)
	{
		a = _a;
		b = _b;
		cout << "Test2 2 I‘m init....\n";
	}
	~Test2()
	{
		cout << "Test2 I‘m Free!\n";
	}
private:
	int a;
	int b;
	Test1 t11;
	Test1 t12;
};

int main()
{
//1 构造函数的初始化列表  解决: 在B类中 组合了一个 A类对象 (A类设计了构造函数)
//根据构造函数的调用规则 设计A的构造函数, 必须要用;没有机会初始化A
//新的语法  Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3)
	Test2 t22(1,3,1,4);
	return 0;
}

[email protected]:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
1 Test1 I‘m init....
4 Test1 I‘m init....
Test2 2 I‘m init....
Test2 I‘m Free!
4 Test1 I‘m Free!
1 Test1 I‘m Free!
[email protected]:~/c++$
时间: 2024-10-29 19:11:44

C++基础3 类:构造 拷贝 析构函数,的相关文章

c++类的拷贝、赋值与销毁(拷贝构造函数、拷贝赋值运算符析构函数)

拷贝构造函数     如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数. 拷贝构造函数第一个参数必须是一个引用类型.此参数几乎总是一个const的引用.拷贝构造函数在几种情况下都会被隐式地使用.因此,拷贝构造函数通常不应该是explicit的. 合成拷贝构造函数 与合成默认构造函数不同,即使我们定义了其他构造函数,编译器也会为我们合成一个拷贝构造函数. 对某些类来说,合成拷贝构造函数用来阻止我们拷贝该类类型的对象.而一般情况,合成的拷贝构造函数

Delphi2010新发现-类的构造和析构函数功能

Delphi2010发布了. 虽然凭着对Delphi的热爱第一时间就安装了,但是现在可能是年纪大了,对新事物缺乏兴趣了.一直都没有仔细研究. 今天有点时间试了一下新功能. 本来C#和Delphi.NET是支持类的构造函数/析构函数的(注意不是实例的构造和析构).也就是在模块初始化/卸载的时候会调用. 这样有很多好处,比如说类的静态变量的初始化什么的都可以在这里做. Delphi For Win32对这方面的需求还不是很大. 第一个原因.历史上旧版Delphi不支持静态变量.只能用Unit的全局变

引用参数与引用返回值 类的拷贝构造

引用地址  http://www.cnblogs.com/bigshow/archive/2008/11/10/1330514.html 经常看到这样的声明:T& func(T& t),这种声明和T func(T t)有什么区别?书上的解释是为了提高效率,究竟是如何提高效率的呢?内部执行了什么操作?本文通过8个小例子对引用参数和引用返回进行了一次彻底的排查.    首先看一下在类的成员函数中的引用参数和引用返回值: 类定义class A{     public:      int x; A

Android中直播视频技术探究之---基础核心类ByteBuffer解析

一.前言 前一篇文章我们介绍了Android中直播视频技术的基础大纲知识,这里就开始一一讲解各个知识点,首先主要来看一下视频直播中的一个重要的基础核心类:ByteBuffer,这个类看上去都知道了,是字节缓冲区处理字节的,这个类的功能非常强大,也在各个场景都有用到,比如网络数据底层处理,特别是结合网络通道信息处理的时候,还有就是后面要说到的OpenGL技术也要用到,当然在视频处理中也是很重要的,因为要处理视频流信息,比如在使用MediaCodec进行底层的视频流编码的时候,处理的就是字节,我们如

读书笔记 effective c++ Item 14 对资源管理类的拷贝行为要谨慎

1. 自己实现一个资源管理类 Item 13中介绍了 “资源获取之时也是初始化之时(RAII)”的概念,这个概念被当作资源管理类的“脊柱“,也描述了auto_ptr和tr1::shared_ptr是如何用堆资源来表现这个概念的.然而并不是所有资源都是在堆上创建的,对于这种资源,像auto_ptr和tr1::shared_ptr这样的智能指针就不适合当作资源句柄(handle)来使用了.你会发现你时不时的就会需要创建自己的资源管理类. 举个例子,假设你正在使用C API来操纵Mutex类型的互斥信

C++的那些事:类的拷贝控制

1,什么是类的拷贝控制 当我们定义一个类的时候,为了让我们定义的类类型像内置类型(char,int,double等)一样好用,我们通常需要考下面几件事: Q1:用这个类的对象去初始化另一个同类型的对象. Q2:将这个类的对象赋值给另一个同类型的对象. Q3:让这个类的对象有生命周期,比如局部对象在代码部结束的时候,需要销毁这个对象. 因此C++就定义了5种拷贝控制操作,其中2个移动操作是C++11标准新加入的特性: 拷贝构造函数(copy constructor) 移动构造函数(move con

15.含有指针成员的类的拷贝(copy constructor)

http://zhedahht.blog.163.com/blog/static/25411174200722710364233/ http://www.cnblogs.com/t427/archive/2012/08/10/2633133.html http://blog.csdn.net/gamecreating/article/details/5382902 http://www.cppblog.com/xczhang/archive/2008/01/21/41569.html 题目:下面

J2SE基础:1.类和对象基础

什么是对象 在Java语言,所有的人,事物或者模块都是一个对象. 相同的对象具有一些相同的特性. 狗,猫,蛇3个对象(动物的对象) 苹果,梨,桔子3个对象(水果的对象) 什么是类 可以将现实生活中的对象经过抽象 这种抽象数据类型称为类. 动物类(Animal) 水果类(Fruit) 类和对象的关系 类是对象的模板(抽象化表示),对象是类的实例化(具体化的展现) 类的组成结构 Java是纯面向对象(除了8种基本数据类型) 而对象是从类产生的.因此类是组成Java程序最基本也是最核心的 元素. 变量

构造与析构函数与=不能被继承,以及内部类的用法

不是所有的函数都能自动地从基类继承到派生类中的.构造函数和析构函数是用来处理对象的创建和析构的,它们只知道对在它们的特殊层次的对象做什么.所以,在整个层次中的所有的构造函数和析构函数都必须被调用,也就是说,构造函数和析构函数不能被继承.另外,operator= 也不能被继承,因为它完成类似于构造函数的活动. //: NINHERIT.CPP -- Non-inherited functions #include <fstream.h> class root { public: root() {