5.3 对象初始化

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 是析构函数。

构造函数的特点如下
  1. 构造函数是成员函数,函数体可写在类体内,也可写在类体外。
  2. 构造函数是特殊的成员函数,该函数的名字与类名相同,不指定返回值类型。
  3. 构造函数可以重载,即可以定义多个构造函数,参数列表不同。
  4. 程序中一般不直接调用构造函数,在创建对象时系统自动调用构造函数。

析构函数

析构函数的特点如下
  1. 析构函数是成员函数,函数体可写在类体内,也可以写在类体外。
  2. 析构函数也是一个特殊的成员函数,它的名字与类名相同,并在前面加“~”字符。析构函数不指定返回值类型,并且没有参数。
  3. 一个类中只能定义一个析构函数。
  4. 析构函数通常被系统自动调用。在对象结束其生命周期时,系统将自动调用析构函数释放该对象。


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;

系统自动调用默认构造函数对 date1date2 两个对象进行初始化。

默认析构函数

如果一个类中没有定义析构函数,则系统提供一个默认析构函数,用来释放对象。系统提供的默认析构函数格式如下:

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++中对象的初始化和生命周期管理。如果有任何问题或需要进一步的解释,请随时留言!

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏驰和徐策

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值