5.3 对象的初始化
前面讲述了通过定义成员函数的方法给对象的数据成员赋值。下面将讲述如何对对象进行初始化。对对象进行初始化通常不使用成员初始化表,而使用构造函数。
5.3.1 构造函数和析构函数
构造函数和析构函数是在类体内声明的两种特殊的成员函数。构造函数的功能是在创建对象时,用给定的值对对象进行初始化。析构函数的功能是用来释放一个对象,与构造函数的功能正好相反。
构造函数
下面将重新定义前面讲过的日期类,并将其定义存放在 tdate.h
文件中。
class TDate {
public:
TDate(int y, int m, int d);
TDate();
void Print();
private:
int year, month, day;
};
TDate::TDate(int y, int m, int d) {
year = y;
month = m;
day = d;
std::cout << "Constructor called." << std::endl;
}
TDate::~TDate() {
std::cout << "Destructor called." << std::endl;
}
void TDate::Print() {
std::cout << year << "." << month << "." << day << std::endl;
}
在类 TDate
的定义中,类体内说明的函数 TDate
是构造函数,~TDate
是析构函数。
构造函数的特点如下
- 构造函数是成员函数,函数体可写在类体内,也可写在类体外。
- 构造函数是特殊的成员函数,该函数的名字与类名相同,不指定返回值类型。
- 构造函数可以重载,即可以定义多个构造函数,参数列表不同。
- 程序中一般不直接调用构造函数,在创建对象时系统自动调用构造函数。
析构函数
析构函数的特点如下
- 析构函数是成员函数,函数体可写在类体内,也可以写在类体外。
- 析构函数也是一个特殊的成员函数,它的名字与类名相同,并在前面加“~”字符。析构函数不指定返回值类型,并且没有参数。
- 一个类中只能定义一个析构函数。
- 析构函数通常被系统自动调用。在对象结束其生命周期时,系统将自动调用析构函数释放该对象。
2024/6/27补充
在做编译原理课成设计设计DFA类时忘了构造函数和析构函数怎么写。
面临的问题就有
(1)构造函数怎么写
解答:
无参构造函数的写法:
1.函数名和类名一致
2.
(2)构造函数该写几种?
就以本题的DFA为例
1. 默认构造函数
默认构造函数不接受任何参数,初始化DFA对象为空。这个构造函数可以确保我们能够创建一个空的DFA对象,然后通过其他方法(如从文件读取或用户输入)来初始化它。
DFA() = default;
2. 带参数的构造函数
带参数的构造函数可以根据传入的参数直接初始化DFA的五元组。这种构造函数适用于在创建DFA对象时就已知所有必要信息的情况。
DFA(const std::set<int>& states, const std::set<char>& alphabet,
const std::map<std::pair<int, char>, int>& transition, int startState,
const std::set<int>& acceptStates)
: states(states), alphabet(alphabet), transition(transition),
startState(startState), acceptStates(acceptStates) {}
3. 拷贝构造函数
拷贝构造函数用于根据现有的DFA对象创建一个新的DFA对象。这在需要复制DFA对象时非常有用。
DFA(const DFA& other)
: states(other.states), alphabet(other.alphabet),
transition(other.transition), startState(other.startState),
acceptStates(other.acceptStates) {}
4. 移动构造函数
移动构造函数用于根据现有的DFA对象创建一个新的DFA对象,并转移资源所有权。这在需要提高性能时非常有用,特别是当对象包含大量数据时。
DFA(DFA&& other) noexcept
: states(std::move(other.states)), alphabet(std::move(other.alphabet)),
transition(std::move(other.transition)), startState(other.startState),
acceptStates(std::move(other.acceptStates)) {
other.startState = -1; // 使原对象处于有效但未初始化状态
}
实际上一般有九种类型:
1. 默认构造函数(无参构造函数)
不接受任何参数,通常用于初始化成员变量为默认值。
DFA() = default;
2. 带参数的构造函数
接受参数并使用这些参数初始化成员变量。
DFA(const std::set<int>& states, const std::set<char>& alphabet,
const std::map<std::pair<int, char>, int>& transition, int startState,
const std::set<int>& acceptStates)
: states(states), alphabet(alphabet), transition(transition),
startState(startState), acceptStates(acceptStates) {}
3. 拷贝构造函数
使用另一个对象的值来初始化新对象。
DFA(const DFA& other)
: states(other.states), alphabet(other.alphabet),
transition(other.transition), startState(other.startState),
acceptStates(other.acceptStates) {}
4. 移动构造函数
用于高效地转移资源所有权,而不是复制资源。
DFA(DFA&& other) noexcept
: states(std::move(other.states)), alphabet(std::move(other.alphabet)),
transition(std::move(other.transition)), startState(other.startState),
acceptStates(std::move(other.acceptStates)) {
other.startState = -1; // 使原对象处于有效但未初始化状态
}
5. 委托构造函数
一个构造函数调用另一个构造函数以简化初始化代码。
DFA() : DFA({}, {}, {}, -1, {}) {} // 调用带参数的构造函数进行初始化
6. 转换构造函数
接受一个参数,并允许将该参数类型转换为类类型。
explicit DFA(int initialState) : startState(initialState) {}
7. 初始化列表构造函数
用于初始化静态常量成员或复杂初始化。
class Example {
private:
const int constantValue;
public:
Example() : constantValue(42) {} // 使用初始化列表初始化常量成员
};
8. 默认参数构造函数
构造函数的参数有默认值,可以根据需要选择性提供参数。
DFA(const std::set<int>& states = {}, const std::set<char>& alphabet = {},
const std::map<std::pair<int, char>, int>& transition = {}, int startState = -1,
const std::set<int>& acceptStates = {})
: states(states), alphabet(alphabet), transition(transition),
startState(startState), acceptStates(acceptStates) {}
9. 带成员初始化的构造函数
适用于包含指针或需要动态分配资源的类,确保资源的正确初始化。
class ComplexResource {
private:
int* data;
public:
ComplexResource() : data(new int[100]) {} // 动态分配内存
~ComplexResource() { delete[] data; } // 释放内存
};
示例
#include <iostream>
#include "tdate.h"
void main() {
TDate today(1998, 4, 9), tomorrow(1998, 4, 10);
std::cout << "Today is ";
today.Print();
std::cout << "Tomorrow is ";
tomorrow.Print();
}
执行该程序后,输出结果如下:
Constructor called.
Constructor called.
Today is 1998.4.9
Tomorrow is 1998.4.10
Destructor called.
Destructor called.
5.3.2 默认构造函数和默认析构函数
默认构造函数
如果没有定义构造函数,则系统会自动生成一个默认构造函数。在一个类中,如果没有定义构造函数,则系统自动生成一个默认构造函数。
例如:
TDate date1, date2;
系统自动调用默认构造函数对 date1
和 date2
两个对象进行初始化。
默认析构函数
如果一个类中没有定义析构函数,则系统提供一个默认析构函数,用来释放对象。系统提供的默认析构函数格式如下:
TDate::~TDate() {}
默认析构函数是一个空函数。
5.3.3 复制构造函数
复制构造函数是用一个已知对象来创造一个新对象,新创建的对象与已知对象的数据成员的值可以相同,也可以不同。复制构造函数只有一个参数并且是对象引用。
复制构造函数的格式
TDate(const TDate &tdate);
示例
将例 5.2 中的点类 TPoint
作如下修改后,存放在名为 tpoint1.h
的头文件中。
class TPoint {
public:
TPoint(int x, int y);
TPoint(const TPoint &p);
~TPoint();
int Xcoord() const { return X; }
int Ycoord() const { return Y; }
private:
int X, Y;
};
TPoint::TPoint(int x, int y) : X(x), Y(y) {}
TPoint::TPoint(const TPoint &p) : X(p.X), Y(p.Y) {
std::cout << "Copy initialization constructor called." << std::endl;
}
TPoint::~TPoint() {
std::cout << "Destructor called." << std::endl;
}
程序示例
#include <iostream>
#include "tpoint1.h"
void main() {
TPoint p1(5, 7);
TPoint p2(p1);
std::cout << "p2 = (" << p2.Xcoord() << ", " << p2.Ycoord() << ")" << std::endl;
}
执行该程序后,输出结果如下:
Copy initialization constructor called.
p2 = (5, 7)
Destructor called.
Destructor called.
总结
对象的初始化是类的关键操作之一,通过构造函数、析构函数和复制构造函数,我们可以灵活地控制对象的创建、销毁和复制行为。希望这些内容能帮助您更好地理解C++中对象的初始化和生命周期管理。如果有任何问题或需要进一步的解释,请随时留言!