1、构造函数
1.1、命名
构造函数的名称必须与类名完全相同。构造函数的名称区分大小写,因此必须与类名的大小写完全匹配。
1.2、用途
初始化对象的成员变量。执行创建对象时所需的其他设置或计算。调用其他初始化方法1.3、生命周期
构造函数在对象创建时自动调用,且只调用一次。构造函数不是由程序员显式调用的,而是由语言的运行时环境在创建对象时自动调用。#include
#include
class Person {
private:
std::string name;
int age;
public:
//无参构造函数
Person() : name("Unknown"), age(0) {}
// 带参数的构造函数
Person(std::string name, int age) : name(name), age(age) {}
// 打印信息的方法
void printInfo() {
std::cout << "Name: " << name << ", Age: " << age << std::endl;
}
};
int main() {
Person person1;
person1.printInfo(); // 输出: Name: Unknown, Age: 0
Person person2("Alice", 30);
person2.printInfo(); // 输出: Name: Alice, Age: 30
return 0;
}
需要使用初始化列表的场景
当类中有引用成员时形参和成员属性同名当类中有const修饰的成员当类中存在其他类的子对象时#include
#include
class Person {
private:
std::string name;
int age;
public:
//无参构造函数
Person() : name("Unknown"), age(0) {}
// 带参数的构造函数
Person(std::string name, int age) : name(name), age(age) {}
// 打印信息的方法
void printInfo() {
std::cout << "Name: " << name << ", Age: " << age << std::endl;
}
};
int main() {
Person person1;
person1.printInfo(); // 输出: Name: Unknown, Age: 0
Person person2("Alice", 30);
person2.printInfo(); // 输出: Name: Alice, Age: 30
return 0;
}
2、析构函数
2.1作用
在对象的生命周期结束时被自动调用,用于执行清理工作,如释放对象所占用的资源(内存、文件句柄、网络连接等)。析构函数确保对象在销毁时不会导致资源泄露或其他问题。
2.2格式
~类名()
{}
2.3特性
自动调用:当对象超出其作用域(如局部对象),或者显式删除对象(如使用delete关键字删除动态分配的对象)时,析构函数会自动被调用。
不能被继承:析构函数不会被继承,每个类都必须有自己的析构函数(如果类中有资源需要清理)。
不能被显式调用:析构函数不能通过对象名或指针显式调用,只能由编译器自动调用。
没有返回类型和参数:析构函数没有返回类型,也不接受任何参数。
默认析构函数:如果一个类没有显式定义析构函数,编译器会提供一个默认析构函数,该析构函数什么也不做。然而,对于包含动态分配内存的类,通常需要定义自己的析构函数来释放这些内存。
2.4示例:
定义一个Person类,有const修饰的成员name;int成员age,char类型的成员sex,定义一个Stu类,包含Person 成员,double *成员,写出Person和Stu的构造和析构函数,每个类写出两种构造函数,实现一个公有的show函数,输出Stu类中属性和Person类中属性的值。
#include
using namespace std;
class Person
{
const string name;
int age;
char sex;
public:
//无参构造,初始化列表
Person():name("lisi"),age(14),sex('m') {}
//有参构造,初始化列表
Person(const string name, int age, char sex) :name(name), age(age), sex(sex)
{}
~Person()//析构函数
{}
void show()
{
cout << name << endl;
cout << age << endl;
cout << sex << endl;
}
};
class Stu
{
Person p1;
double* p2;
public:
Stu():p2(new double) {} //无参构造,初始化列表
//有参构造,初始化列表
Stu(const string name, int age, char sex, double score) :p1(name, age, sex), p2(new double(score))
{}
~Stu()//析构函数
{
delete p2;
}
void show()
{
p1.show();
cout << *p2 << endl;
}
};
int main()
{
//Stu s1;
//s1.show();
Stu s2("zhangsan", 20, 'm', 23.9);
s2.show();
return 0;
}
3、拷贝构造函数
3.1定义
用于创建一个新对象,并将其初始化为另一个同类型对象的副本
3.2格式
类名(const 类名 &other) //拷贝构造函数的参数是同类对象的引用
{}
3.3示例:
在2.4的基础上,写出Person类和Stu类的拷贝构造函数。
#include
#include
using namespace std;
class Person
{
const string name;
int age;
char sex;
public:
//无参构造,初始化列表
Person():name("lisi"),age(14),sex('m') {}
//有参构造,初始化列表
Person(const string name, int age, char sex) :name(name), age(age), sex(sex)
{}
~Person()//析构函数
{}
void show()
{
cout << name << endl;
cout << age << endl;
cout << sex << endl;
}
//拷贝构造函数
Person(const Person& other):name(other.name),age(other.age),sex(other.sex)
{
//name为常量字符串成员,在对象的生命周期内其值是不可修改,
// 应该在初始化列表中为常量成员变量赋值,这是C++中初始化常量成员的唯一途径
}
};
class Stu
{
Person p1;
double* p2;
public:
Stu():p2(new double) {} //无参构造,初始化列表
//有参构造,初始化列表
Stu(const string name, int age, char sex, double score) :p1(name, age, sex), p2(new double(score))
{}
~Stu()//析构函数
{
delete p2;
}
void show()
{
p1.show();
cout << *p2 << endl;
}
Stu(const Stu& other):p1(other.p1),p2(new double(*(other.p2)))
{
}
};
int main()
{
//Stu s1;
//s1.show();
Stu s2("zhangsan", 20, 'm', 23.9);
s2.show();
return 0;
}
3.4深浅拷贝
浅拷贝
浅拷贝是指创建一个新的对象,但新对象中的元素仍然是对原始对象中元素的引用。也就是说,浅拷贝只复制了容器本身,而不复制容器内部的元素。
深拷贝
深拷贝是指创建一个新的对象,并递归地复制它包含的所有对象,直到所有的元素都是新对象。这意味着深拷贝不仅复制了容器本身,还复制了容器内部的所有元素(以及这些元素内部可能包含的所有元素,依此类推)。
类中包含指针成员时,拷贝构造函数,让不同类对象中的指针成员指向相同的空间(浅拷贝),存在的问题:对于堆空间二次释放,资源抢占。系统默认提供的拷贝构造函数是浅拷贝。
何时发生深浅拷贝问题:
类中存在指针成员时,拷贝构造和拷贝赋值函数会发生深浅拷贝的问题
浅拷贝:两个不同类对象的指针成员,指向同一片空间,会造成资源抢占,析构释放多次的问题
深拷贝:两个不同类对象的指针成员,指向不同的空间,但是空间中存储的内容相同
4.拷贝赋值函数
4.1定义和功能
将一个对象的状态复制到另一个同类型的对象中。这个函数通常具有与类名相同的名称,并接受一个同类型的常量引用作为参数,同时返回对类对象的引用(通常带有const限定符,但在赋值操作符中不需要这样做,因为赋值操作本身会修改左侧对象)。
使用类对象给已有的类对象赋值时会自动调用拷贝赋值函数
4.2格式
类名 &operator=(const 类名 &other)
{
//函数体
}
4.3示例
在3.3的基础上完成拷贝赋值函数
#include
#include
using namespace std;
class Person
{
const string name;
int age;
char sex;
public:
//无参构造,初始化列表
Person():name("lisi"),age(14),sex('m') {}
//有参构造,初始化列表
Person(const string name, int age, char sex) :name(name), age(age), sex(sex)
{}
~Person()//析构函数
{}
void show()
{
cout << name << endl;
cout << age << endl;
cout << sex << endl;
}
//拷贝构造函数
Person(const Person& other):name(other.name),age(other.age),sex(other.sex)
{
//name为常量字符串成员,在对象的生命周期内其值是不可修改,
// 应该在初始化列表中为常量成员变量赋值,这是C++中初始化常量成员的唯一途径
}
//拷贝赋值函数
Person& operator=(const Person& other)
{
//const修饰的name成员无法被赋值
if (&other != this)
{
this->age = other.age;
this->sex = other.sex;
}
return *this;
}
};
class Stu
{
Person p1;
double* p2;
public:
Stu():p2(new double) {} //无参构造,初始化列表
//有参构造,初始化列表
Stu(const string name, int age, char sex, double score) :p1(name, age, sex), p2(new double(score))
{}
~Stu()//析构函数
{
delete p2;
}
void show()
{
p1.show();
cout << *p2 << endl;
}
//拷贝构造函数,调用其他类的拷贝构造函数时,也需要传同类引用
Stu(const Stu& other):p1(other.p1),p2(new double(*(other.p2)))
{
}
//拷贝赋值函数
Stu& operator=(const Stu& other)
{
if (&other != this)
{
//显性调用Person类的拷贝赋值函数,给Person类中的成员赋值
p1.operator=(other.p1);
*(this->p2) = *(other.p2);
}
return *this;
}
};
int main()
{
//Stu s1;
//s1.show();
Stu s2("zhangsan", 20, 'm', 23.9);
s2.show();
Stu s3 = s2;
s3.show();
return 0;
}