为什么会出现构造函数 与 析构函数
chunli@Linux:~/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;
}
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out类的构造函数 与 析构函数
chunli@Linux:~/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;
}
chunli@Linux:~/c++$ g++ -g -Wall main.cpp && ./a.out
构造函数
构造函数
Hello World
析构函数
析构函数构造函数初始化变量:
析构函数释放内存
chunli@Linux:~/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;
}
chunli@Linux:~/c++$ g++ -Wall -g main.cpp && ./a.out
构造函数
构造函数
10
Hello Linux!
析构函数
析构函数
-------------
chunli@Linux:~/c++$类的构造函数3种初始化方式:
chunli@Linux:~/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;
}
正常编译:
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out
没有默认参数的构造函数
a=1 b=2带有2个默认参数的构造函数
a=7带有1个默认参数的构造函数
a=1 b=4带有2个默认参数的构造函数
赋值构造函数构造函数
析构函数
析构函数
析构函数
析构函数
析构函数
-------------
g++ -Wall编译
chunli@Linux:~/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个默认参数的构造函数
赋值构造函数构造函数
析构函数
析构函数
析构函数
析构函数
析构函数
-------------
chunli@Linux:~/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;
}
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out
2个参数
a=1 b=5
拷贝构造函数被调用
a=-336889536 b=32764拷贝构造函数的使用:
第1种调用方法: Test t2 = t1;
chunli@Linux:~/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;
}
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out
2个参数
a=1 b=5
拷贝构造函数被调用
a=101 b=151拷贝构造函数的第2 中调用方式:Test t2(t1);
chunli@Linux:~/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;
}
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out
2个参数
a=1 b=5
拷贝构造函数被调用
a=101 b=151【答疑】对象的赋值操作t2 = t1 ,不会调用赋值构造函数:
chunli@Linux:~/c++$ cat main.cpp
chunli@Linux:~/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;
}
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out
2个参数
a=1 b=5
2个参数
a=99 b=99
a=1 b=5赋值构造函数的第3中应用:函数
chunli@Linux:~/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;
}
chunli@Linux:~/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编译运行:
chunli@Linux:~/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下编译运行:
chunli@Linux:~/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下,不会出现这种情况
chunli@Linux:~/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;
}
chunli@Linux:~/c++$
GCC编译运行:
chunli@Linux:~/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析构函数被调用
chunli@Linux:~/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 ;
}【回顾上午】
构造函数初始化的三种方式:
chunli@Linux:~/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;
}
chunli@Linux:~/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编译有些区别:
chunli@Linux:~/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编译执行:
chunli@Linux:~/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析构函数被调用
chunli@Linux:~/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,【当你定义了构造函数,你必须使用】:
先演示没有任何构造函数的情景:
chunli@Linux:~/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:
chunli@Linux:~/c++$ g++ -Wall -g main.cpp && ./a.out
执行对象的成员函数 a=2071403600 b=32765当类中定义了构造函数,如果不使用,对象初始化就会报错!
示范:
chunli@Linux:~/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;
}
编译运行:直接报错
chunli@Linux:~/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();
^手动定义一个空的构造函数,编译通过!
chunli@Linux:~/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;
}
chunli@Linux:~/c++$ g++ -Wall -g main.cpp && ./a.out
执行对象的成员函数 a=89452272 b=32765
chunli@Linux:~/c++$【结论】
在类的定义时,只要写了构造函数,编译器就不会再自动为你定义无参的构造函数,这个构造函数必须要使用!
构造析构阶段性总结
1)构造函数是C++中用于初始化对象状态的特殊函数
2)构造函数在对象创建时自动被调用
3)构造函数和普通成员函数都遵循重载规则
4)拷贝构造函数是对象正确初始化的重要保证
5)必要的时候,必须手工编写拷贝构造函数
【浅拷贝 与 深拷贝】
浅拷贝 示范,程序编译通过,运行就死掉,
因为释放已经释放过的内存,所以死掉
chunli@Linux:~/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;
}
编译运行:
chunli@Linux:~/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)
chunli@Linux:~/c++$
gdb调试:
chunli@Linux:~/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);手动写一个拷贝构造函数吗,完成深拷贝:
chunli@Linux:~/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;
}
编译运行:
chunli@Linux:~/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,继续宕机!
chunli@Linux:~/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;
}
编译通过,运行出错!
chunli@Linux:~/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调试】:
chunli@Linux:~/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,被组合对象的析构顺序与构造顺序相反
chunli@Linux:~/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;
}
chunli@Linux:~/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!
chunli@Linux:~/c++$本文出自 “魂斗罗” 博客,请务必保留此出处http://990487026.blog.51cto.com/10133282/1794395
原文:http://990487026.blog.51cto.com/10133282/1794395