目录
构造函数的概念
1构造函数定义及调用
1)C++中的类可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数;
2)构造函数在定义时可以有参数;
3)没有任何返回类型的声明。
2构造函数的调用
自动调用:一般情况下C++编译器会自动调用构造函数
手动调用:在一些情况下则需要手工调用构造函数
析构函数的概念
3)析构函数定义及调用
1)C++中的类可以定义一个特殊的成员函数清理对象,这个特殊的成员函数叫做析构函数
语法:~ClassName()
2)析构函数没有参数也没有任何返回类型的声明
3)析构函数在对象销毁时自动被调用
4)析构函数调用机制
C++编译器自动调用
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Test
{
public:
Test() //无参数构造函数
{
p = (char *)malloc(100);
strcpy(p, "aaaa");
a = 10;
cout << "我是构造函数 被执行了" << endl;
}
void print()
{
cout << p << endl;
cout << a << endl;
}
~Test()
{
if (p)
{
free(p);
p = NULL;
}
cout << "我是析构函数 被执行了" << endl;
}
private:
int a;
char *p;
};
//给对象搭建一个舞台,研究对象的行为
void objplay()
{
Test t1;
t1.print();
}
int main()
{
objplay();
cout << "hello" << endl;
system("pause");
return 0;
}
我们可以看到当定义一个变量的时候,c++编译器会自动调用他的构造函数,当变量销毁的时候就自动调用了析构函数。
有参构造函数的使用
构造函数主要分为三类,即:默认构造函数、拷贝构造函数、有参构造函数。
有参构造函数的初始化主要有三种:括号法、=号法、直接调用构造函数,手动的调用构造函数
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Test2
{
public:
Test2()
{
m_a = 0;
m_b = 0;
cout << "无参数构造函数" << endl;
}
Test2(int a,int b)
{
m_a = a;
m_b = b;
cout << "有参数构造函数" << endl;
}
Test2(int a)
{
m_a = a;
m_b = 0;
cout << "有参数构造函数" << endl;
}
//赋值构造函数(copy构造函数)
Test2(const Test2 &obj)
{
cout << "我也是构造函数" << endl;
}
void prinT()
{
cout << "普通变量函数" << endl;
}
private:
protected:
int m_a;
int m_b;
};
int main()
{
//1括号法
Test2 t1(3, 4); //c++编译器帮我们自动调用构造函数
t1.prinT();
//2=号法
Test2 t2 = (3, 4); //c++对等号操作符进行功能增强
//3 直接调用构造函数,手动的调用构造函数
Test2 t3 = Test2(1, 2); //匿名对象
cout << "hello" << endl;
system("pause");
return 0;
}
这边解释一下括号法,由于括号表达式取的是最后一个操作符的值,即Test2 t2= (3,4)等价于
Test2 t2 = 4;所以调用的是一个参数的有参构造函数。
拷贝构造函数的使用
拷贝构造函数主要有四种使用场景
第一种:定义变量初始化
第二种:括号法
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Test4
{
public:
Test4()
{
m_a = 0;
m_b = 0;
cout << "无参数构造函数" << endl;
}
Test4(int a, int b)
{
m_a = a;
m_b = b;
cout << "有参数构造函数" << endl;
}
Test4(int a)
{
m_a = a;
m_b = 0;
cout << "有参数构造函数" << endl;
}
//赋值构造函数(copy构造函数)
Test4(const Test4 &obj)
{
cout << "我也是构造函数" << endl;
m_a = obj.m_a + 100;
m_b = obj.m_b + 100;
}
void prinT()
{
cout << "普通变量函数" << endl;
cout << "m_a" << m_a << endl;
cout << "m_b" << m_b << endl;
}
private:
int m_a;
int m_b;
protected:
};
//1 赋值构造函数 用1个对象去初始化另外一个对象
int main()
{
Test4 t0(1, 2);
Test4 t1(1, 2);
t0 = t1; //用t1给t0赋值,和初始化是两个不同的概念
//第一种调用方法
Test4 t2 = t1; //用t1来初始化t2
t2.prinT();
//第二种调用方法
Test4 t3(t1);
t3.prinT();
cout << "hello" << endl;
system("pause");
return 0;
}
第一种调用方法是利用其他变量来初始化,这种方法需要注意的是需要在定义变量的时候就初始化该变量,如果在定义后在进行赋值操作的话就不是调用拷贝构造函数,调用的就是重载=操作符的方法。
第二种方法利用的是括号法,即在初始化的时候给定参数进行调用。
第三种方法是利用实参初始化形参的调用拷贝构造函数。
#define _CRT_SECURE_NO_WARNINGS
#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;
};
//业务函数
void f(Location p)
{
cout << p.GetX() << endl;
}
void playobj()
{
Location a(1, 2);
Location b = a;
cout << "b对象已经初始化完毕" << endl;
f(b); //b实参去初始化形参p,会调用copy构造函数
}
int main()
{
playobj();
cout << "hello" << endl;
system("pause");
return 0;
}
我们可以看到在执行f函数的时候会先调用拷贝构造函数初始化形参p ,在调用函数体。
第四种方法是利用函数的返回值来调用拷贝构造函数
#define _CRT_SECURE_NO_WARNINGS
#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;
};
//业务函数
void f(Location p)
{
cout << p.GetX() << endl;
}
void playobj51()
{
Location a(1, 2);
Location b = a;
cout << "b对象已经初始化完毕" << endl;
f(b); //b实参去初始化形参p,会调用copy构造函数
}
int main51()
{
playobj51();
cout << "hello" << endl;
system("pause");
return 0;
}
我们可以看到g函数在返回值的时候调用了拷贝构造函数返回一个匿名对象,这是由于A变量是存放在栈空间中的,所以他的生存周期只有在g函数里,所以返回他在外部无法使用,明显不合适,所以c++编译器返回的一个拷贝构造函数的匿名对象。
这边在接收匿名对象的时候也有两种情况
一种是:用匿名对象初始化m,此时c++编译器直接把匿名对象转成变量
另一种是:用匿名对象赋值给变量,然后匿名对象析构
这个是第一种情况 ,此时c++编译器把匿名对象直接转化成m变量,所以只有在函数结束的时候才会调用析构函数。
这是第二种情况, 此时由于调用了赋值操作,所以匿名函数在赋值后直接被析构掉。
特殊情况
二个特殊的构造函数
1)默认无参构造函数
当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空
2)默认拷贝构造函数
当类中没有定义拷贝构造函数时,编译器默认提供一个默认拷贝构造函数,简单的进行成员变量的值复制
1)当类中没有定义任何一个构造函数时,c++编译器会提供默认无参构造函数和默认拷贝构造函数
2)当类中定义了拷贝构造函数时,c++编译器不会提供无参数构造函数
3) 当类中定义了任意的非拷贝构造函数(即:当类中提供了有参构造函数或无参构造函数),c++编译器不会提供默认无参构造函数
4 )默认拷贝构造函数成员变量简单赋值
总结:只要你写了构造函数,那么你必须用。
1)构造函数是C++中用于初始化对象状态的特殊函数
2)构造函数在对象创建时自动被调用
3)构造函数和普通成员函数都遵循重载规则
4)拷贝构造函数是对象正确初始化的重要保证
5)必要的时候,必须手工编写拷贝构造函数
引用上面的案例 ,如果我们把有参构造函数的默认值去掉的话,在初始化对象的时候没有给参数的话,就会出现没有默认构造函数可以调用的情况,这就是由于当类中定义了任意的非拷贝构造函数(即:当类中提供了有参构造函数或无参构造函数),c++编译器不会提供默认无参构造函数,所以出现错误。