构造函数和析构函数
初始化与清理
当大家买手机电脑的时候,都有一个初始化设置,也就是说这些产品当创建的时候都有一个基础值。随着时间推移,手机电脑用的越久文件越多,当我们不用的时候要清理,删除掉手机电脑中的数据,保护隐私。
C++中面对对象思想中这种方法,对事物抽象,比如当创建一个对象的时候,有的数据会有初始状态,当对象销毁的时候会销毁自己创建的数据。
从初始化与清理使两个重要操做,当初始化的时候,数据内容是未知的,如果没有清理,也会造成安全问题。所以引出构造函数与析构函数,这两个函数是编译器自动调用,完成初始化与清理。
构造函数语法
构造函数主要作用是为对象的成员属性赋值,构造函数由编译器自动调用,不用手动调用。
构造函数函数名和类名相同,没有返回值,不能加void,但可以有参数,可以重载
class A
{
public:
//构造函数
A()
{
c = 20;
}
//可以有参数
A(int x)
{
C = X;
}
private:
int c;
};
析构函数语法
析构函数主要在对象销毁前系统自动调用,执行一些清理工作。
析构函数函数名和类名相同,在函数前面加’~"符号,不能有void,不能有参数,不能重载
class B
{
//构造函数
B()
{
cout << "构造函数" << endl;
}
//析构函数
~B()
{
cout << "析构函数" << endl;
}
};
class Person //人的类
{
public:
//使用构造函数申请内存,并进行初始话
Person()
{
m_Name = (char*)malloc(sizeof("MyName"));
strcpy(m_Name, "MyName");
m_Tall = 185;
m_Money = 10000;
}
//使用析构函数释放内存
~Person()
{
if(m_Name != NULL)
{
free(m_Name);
m_Name = NULL;
}
}
public:
char* m_Name;
int m_Tall;
int m_Money;
};
void test()
{
Person p;
cout << p.m_Name << endl;
cout << p.m_Tall << endl;
cout << p.m_Money << endl;
}
构造函数的分类及调用
1.构造函数的分类:
无参构造函数,有参构造函数,拷贝构造函数
2.默认类默认提供了 默认构造函数,默认析构函数,默认拷贝构造函数,默认赋值函数
class test
{
public:
test()
{
cout << "调用无参构造函数" << endl;
age = 0;
}
test(int a)
{
age = a;
cout << "调用有参构造函数" << endl;
}
t_printf()
{
cout << "age = " << age;
}
test(const test& t)//参数要写成引用
{
cout << "调用拷贝构造函数" << endl;
age = t.age;
}
~test()
{
cout << "析构函数"
}
private:
int age;
};
int main
{
//调用无参构造函数
test t;
t.t_printf();
//无参构造错误的调用的方式
test t1();
t1.t_printf();
//调用有参构造函数
test t2(18);//常用这种方法
t2.t_printf();
//也可以使用这种方法调用
//test t2 = 18;
//test t2 = test(18);
//调用拷贝构造函数,常用括号法调用拷贝构造函数
test t3(t2);//常用这种方法
t3.t_printf();
//还可以使用=法调用
//test t3 = t2;
//test t3 = test(t2);
return 0;
}
拷贝构造函数中形参必须是引用
如果不使用引用会发生以下情况
class test
{
public:
test()
{
cout << "调用无参构造函数" << endl;
}
test(int a)
{
cout << "调用有参构造函数" << endl;
}
test(const test t)
{
cout << "调用拷贝构造函数" << endl;
age = t.age;
}
~test()
{
cout << "析构函数"
}
private:
int age;
};
int main()
{
test t1;
test t2(t1);
//调用的时候会发生隐式转换
/*test(const test t)
{
cout << "调用拷贝构造函数" << endl;
age = t.age;
}*/
//const test t = t1;
//const test t(m1);
//const test t = t1;
//进入死循环
return 0;
}
匿名对象
匿名对象,就是没有名字的对象
class test
{
public:
test()
{
cout << "构造函数" << endl;
}
test(int x)
{
a = x;
cout << "有参构造函数" << endl;
}
~test()
{
cout << "析构函数" << endl;
}
int a;
};
void test01
{
test(10);//匿名对象,没有名字
//匿名对象的声明周期在当前行。
test t1 = test(10); //如果匿名对象有对象名字来接,就不是匿名对象了。
cout << "test01 end" << endl;
}
拷贝构造函数的调用时机
1.对象以值传递的方式传给函数参数
class test
{
public:
test()
{
cout << "无参构造函数" << endl;
}
test(int a)
{
cout << "有参构造函数" << endl;
}
test(const test &t)
{
cout << "拷贝构造函数" << endl;
}
~test()
{
cout << "析构函数" << endl;
}
};
//1.对象以值方式给函数参数
void func(test t)//test t=t1;
{
}
void test01()
{
Maker t1;
func(t1);
2.函数的局部对象以值的方式从函数返回,vs Debug(调试)模式下,会调用拷贝构造,vs Release(发行)模式下不会调用拷贝构造,qt也不调用
test func2()
{
//局部对象
test t;
cout << "局部对象的地址:" << &t << endl;
return t;
}
void test02()
{
test t2 = func2();
cout << "t2对象的地址:" << &t2 << endl;
}
Debug模式运行结果
Release模式运行结果
3.用一个对象初始化另一个对象
void test03()
{
test t3;
test t(t3);
}
构造函数调用规则
一、默认情况下C++编译器为我们加三个函数。
1.默认构造函数(无参,函数体为空)
2.默认拷贝构造函数(对类内非静态简单属性进行拷贝)
3.默认析构函数(无参,函数体为空)
二、如果用户定义了拷贝构造函数,编译器不会再提供任何默认的拷贝构造函数。
class T
{
public:
//用户提供拷贝构造函数
T(const T& t)
{
}
};
void test01()
{
//T t;//错误
}
三、如果用户定义了普通构造(非拷贝),编译器不会提供默认构造函数,会提供默认拷贝构造函数。
class T2
{
public:
//用户提供有参构造函数
T2(int x)
{
a = x;
}
int a;
};
void test02
{
T2 t(10);//正确,调用有参构造
//T2 t1;错误
T2 t2(t);//调用默认构造函数
}
explicit关键字
C++提供了关键字explicit,禁止通过构造函数进行的隐式转换。声明为explic的构造函数的构造函数不能在隐式转换中使用。
explicit注意事项
1.explicit用于修饰构造函数,防止隐式转换
2.是针对单参数的构造函数(或除了第一个参数外其余参数都有默认值的多参构造函数)
class Mstring
{
public:
explicit Mstring(int a)
{
cout << "Mstring(int a)" << endl;
}
/*针对单参数的构造函数,这样的使用也是正确的
explicit Mstring(int a, int b = 2, int c = 3)
{
cout << "Mstring(int a, int b = 2, int c = 3)" << endl;
}
*/
Mstring(const char *str)
{
cout << "Mstring(char* str)" << endl;
}
};
void test()
{
//Mstring s1 = 10; //产生错误不能进行隐式转换
Mstring s2(20);//正确
Mstring s3 = "abc";//正确
Mstring s4("abc");//正确
}