构造函数的概念:
构造函数是类中一种特殊的函数,构造函数的功能有:
(1)创见对象
(2)对属性进行初始化
如果程序员不在类中写构造函数,编译器会自动添加一个没有参数的、函数体为空的构造函数
注意:
(1)构造函数不写返回值,因为返回值是创建的对象
(2)函数名称必须与类名相同
代码示例:
#include <iostream>
using namespace std;
class MobilePhone
{
private:
string brand = "华为";
string model = "p50";
int weight = 188;
public:
MobilePhone() // 如果不写,编译器自动添加
{
cout << "创建了一个对象" << endl; // 默认的构造函数没有这句
}
void show()
{
cout << brand << " " << model << " " << weight << endl;
}
};
int main()
{
MobilePhone mp1; // 调用默认构造函数
mp1.show();
return 0;
}
构造函数重载:
构造函数支持重载,如果程序员手写任何一个构造函数,则编译器不再添加默认的无参构造函数
此时如果仍要调用无参构造函数且手写的不是无参的构造函数时,则需要利用构造函数重载来实现多个构造函数
构造函数的调用是强制的,一旦在类中定义了构造函数,那么创建对象时就一定要调用,不调用就是错误的。如果有多个重载的构造函数,那么创建对象时提供的实参必须和其中的一个构造函数匹配;反过来说,创建对象时只有一个构造函数被调用
代码示例:
#include <iostream>
using namespace std;
class MobilePhone
{
private:
string brand;
string model;
int weight;
public:
MobilePhone(string b,string m,int w)
{
brand = b;
model = m;
weight = w;
}
MobilePhone()
{
brand = "华为";
model = "P50";
weight = 188;
}
void show()
{
cout << brand << " " << model << " " << weight << endl;
}
};
int main()
{
MobilePhone mp1("小米","13",188);
mp1.show(); // 小米 13 188
MobilePhone* mp2 = new MobilePhone("苹果","14",177); //堆内存对象,手动开辟手动释放
mp2->show();
delete mp2; // 苹果 14 177
MobilePhone mp3;
mp3.show(); // 华为 P50 188
return 0;
}
构造初始化列表:
构造初始化列表与构造函数的函数体的区别
(1)如果成员变量是const修饰的,不能使用构造函数的函数体赋值
(2)构造初始化列表要比函数体中的赋值执行效率更高
如果构造初始化列表影响了代码的可读性,也可以考虑不使用或者混用
代码示例:
#include <iostream>
using namespace std;
class MobilePhone
{
private:
string brand;
string model;
int weight;
public:
// 使用构造初始化列表赋值
MobilePhone(string b,string m,int w):brand(b),model(m),weight{w}{}
void show()
{
cout << brand << " " << model << " " << weight << endl;
}
};
int main()
{
MobilePhone mp1("小米","13",188);
mp1.show(); // 小米 13 188
return 0;
}
混用也是可以的
#include <iostream>
using namespace std;
class MobilePhone
{
private:
string brand;
string model;
int weight;
public:
// 混用
MobilePhone(string b,string m,int w):brand(b),weight{w}
{
model = m;
}
void show()
{
cout << brand << " " << model << " " << weight << endl;
}
};
int main()
{
MobilePhone mp1("小米","13",188);
mp1.show(); // 小米 13 188
return 0;
}
拷贝构造函数:
如果程序员不手写拷贝构造函数,编译器会自动添加一个拷贝构造函数,与其他的构造函数重载,以便于对象的拷贝构建
拷贝构造函数分为两类:
(1)浅拷贝
浅拷贝只进行赋值,如果对象中的某个成员是指针类型数据,直接复制操作会导致两个指针保存的地址相同,指向同一份内存,这不符合面向对象的特性
浅拷贝中,如果对象中的某个成员是指针类型数据,且所指之物在堆区创建,则使用浅拷贝仅仅是对指针的赋值,让指针也指向这一块内存空间,在析构的时候,会造成析构同一块内存空间多次引起程序崩溃
示例代码:
#include <iostream>
#include <string.h>
using namespace std;
class Dog
{
private:
char* name; // 指针
public:
Dog(char* n)
{
name = n;
}
// 复现默认的拷贝构造函数
Dog(const Dog& d)
{
name = d.name; // 浅拷贝
}
void show_name()
{
cout << name << endl;
}
};
int main()
{
char c[20] = "wangcai";
Dog d1(c);
Dog d2(d1); // 拷贝构造函数
strcpy(c,"xiaobai");
d1.show_name(); // xiaobai
d2.show_name(); // xiaobai
return 0;
}
(2)深拷贝
深拷贝首先开辟和源对象大小相同的一块空间,然后将源对象拷贝到目标中去,这样指针成员就指向了不同的地址空间,不会出现析构多次同一空间的情况
代码示例:
#include <iostream>
#include <string.h>
using namespace std;
class Dog
{
private:
char* name; // 指针
public:
Dog(char* n)
{
name = new char[20];
strcpy(name,n);
}
Dog(const Dog& d)
{
name = new char[20]; // 深拷贝
strcpy(name,d.name);
}
void show_name()
{
cout << name << endl;
}
};
int main()
{
char c[20] = "wangcai";
Dog d1(c);
Dog d2(d1); // 拷贝构造函数
strcpy(c,"xiaobai");
d1.show_name(); // wangcai
d2.show_name(); // wangcai
return 0;
}
析构函数:
析构函数是与构造函数独立的函数,在同一个对象销毁时,析构函数可以自动被调用,通常在析构函数中执行一些对象占用资源的回收等收尾工作
代码示例:
#include <iostream>
using namespace std;
class Cat
{
private:
string name;
public:
Cat(string n)
{
name = n;
}
~Cat()
{
cout << name << "猫死了" << endl;
}
};
int main()
{
Cat* c3 = new Cat("丙");
// 独立代码块:表示一个局部范围
{
Cat c1("甲");
cout << "}" << endl;
}
Cat c2("乙");
delete c3;
cout << "主函数结束" << endl;
return 0;
}