C++--类与对象(二)

类的六个成员函数

如果一个类中什么成员都没有,简称为空类。
空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员
函数。
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。
class Date {}; //空类

构造函数

构造函数是一种特殊的成员函数,在C++中用于初始化对象的数据成员。当创建一个类的对象时,构造函数会自动调用,用于确保对象被正确初始化。构造函数的主要作用是为对象提供合适的初始状态,使其能够正常工作。

构造函数的概念可以总结为以下几点:

  1. 初始化对象:构造函数负责为对象的数据成员赋予合适的初始值,确保对象处于一个有效的状态。

  2. 与类同名:构造函数的名称与类名相同,因此可以用来标识对象的类型。

  3. 没有返回类型:与普通函数不同,构造函数没有返回类型,包括 void。因为它们的作用是初始化对象,而不是返回值。

  4. 自动调用:构造函数在对象创建时自动调用,无需手动调用。当对象被声明时,构造函数会立即执行。

  5. 可以有多个重载:同一个类可以有多个构造函数,它们可以根据参数的不同进行重载。这样可以提供多种不同的初始化方式。

  6. 可以带默认参数:构造函数可以像普通函数一样拥有默认参数,从而提供更灵活的对象初始化方式。

构造函数在类的设计中非常重要,它决定了对象在创建时的初始状态,为后续的操作提供了基础。

#include <iostream>
using namespace std;
class Date
{
public:
	void Print()
	{
		cout << _year << "年" << _month << "月" << _day << "日" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1; // 编译器将调用自动生成的默认构造函数对d1进行初始化
	d1.Print();
	return 0;
}

这段代码定义了一个简单的日期类 Date,其中包含了三个私有数据成员 _year_month_day,以及一个公有成员函数 Print() 用于输出日期信息。

main() 函数中,创建了一个 Date 类型的对象 d1,由于没有显式地定义构造函数,编译器会自动生成一个默认构造函数。默认构造函数会对对象的数据成员进行默认初始化,对于基本数据类型,如 int,默认初始化会使其值未定义,即可能是随机值。然后调用对象的 Print() 函数输出日期信息。

这段代码的运行结果可能是这样的(由于 _year_month_day 的值未定义,输出的结果会有所不同)

虽然在我们不写的情况下,编译器会自动生成构造函数,但是编译器自动生成的构造函数可能达不到我们想要的效果,所以大多数情况下都需要我们自己写构造函数。

析构函数

析构函数是一种特殊的成员函数,在C++中用于释放对象占用的资源,并在对象生命周期结束时自动调用。它的作用与构造函数相反,用于清理对象在生命周期中分配的资源,以防止资源泄漏和内存泄漏。

析构函数的概念可以总结为以下几点:

  1. 释放资源:析构函数负责释放对象占用的资源,包括动态分配的内存、打开的文件、建立的网络连接等。它确保在对象被销毁之前,所有分配的资源都被正确释放,以避免资源泄漏。

  2. 与类同名:析构函数的名称由波浪号(~)后跟类名构成,与构造函数相似,但前面有一个波浪号。

  3. 没有参数:析构函数不接受任何参数,也没有返回类型,包括 void。它只负责清理对象的资源,不返回任何值。

  4. 自动调用:析构函数在对象生命周期结束时自动调用,无需手动调用。当对象离开其作用域、被删除或程序结束时,析构函数会自动执行。

  5. 只有一个:每个类只能有一个析构函数,不允许重载。如果没有显式地定义析构函数,编译器会自动生成一个默认的析构函数。

析构函数在类的设计中非常重要,它确保对象在销毁时资源被正确释放,避免内存泄漏和资源泄漏问题。

class Date
{
public:
	Date()// 构造函数
	{}
	~Date()// 析构函数
	{}
private:
	int _year;
	int _month;
	int _day;
};
  • 析构函数 ~Date():这是一个析构函数,用于在对象被销毁时释放资源。在这个示例中,析构函数也是一个空函数,没有实际的清理工作。因为在这个简单的示例中,Date 类并没有分配动态资源,所以不需要显式地释放资源。然而,通常情况下,析构函数会负责释放类对象可能持有的资源,如动态分配的内存、打开的文件句柄等。

 拷贝构造函数

拷贝构造函数是一种特殊的成员函数,在C++中用于通过复制已存在对象的数据创建新对象。当一个对象被传递给函数参数、以值传递方式返回或者通过赋值操作符进行对象赋值时,拷贝构造函数会被调用。

拷贝构造函数的概念可以总结为以下几点:

  1. 复制对象:拷贝构造函数的主要作用是创建一个新对象,并将已存在对象的数据成员复制到新对象中。这样可以在不修改原始对象的情况下创建一个相同状态的新对象。

  2. 参数为同类对象的引用:拷贝构造函数的参数通常是对同类对象的引用,表示要复制的对象。

  3. 对象传递和赋值时调用:拷贝构造函数会在对象被传递给函数参数、以值传递方式返回或者通过赋值操作符进行对象赋值时自动调用。

  4. 默认浅拷贝:默认情况下,拷贝构造函数执行浅拷贝,即简单地将原始对象的数据成员复制到新对象中。如果类中存在指针成员等资源,可能需要手动编写拷贝构造函数来实现深拷贝,以防止资源重复释放或指针悬空等问题。

拷贝构造函数在类的设计中非常重要,它允许我们以简单、方便的方式创建对象的副本,并在对象传递和赋值过程中确保数据的正确复制。

#include <iostream>

class MyClass {
private:
    int _data;
public:
    // 构造函数
    MyClass(int data) : _data(data) {
        std::cout << "Constructor called for object with data: " << _data << std::endl;
    }

    // 拷贝构造函数
    MyClass(const MyClass& other) : _data(other._data) {
        std::cout << "Copy constructor called for object with data: " << _data << std::endl;
    }

    // 打印数据成员
    void printData() const {
        std::cout << "Data: " << _data << std::endl;
    }
};

int main() {
    // 创建一个对象并初始化
    MyClass obj1(10);
    std::cout << "Object 1: ";
    obj1.printData();

    // 使用拷贝构造函数创建一个新对象
    MyClass obj2 = obj1;
    std::cout << "Object 2: ";
    obj2.printData();

    return 0;
}

这个示例包含了以下特点:

  1. 构造函数:类 MyClass 包含一个构造函数,用于初始化对象的数据成员 _data

  2. 拷贝构造函数:类 MyClass 包含一个拷贝构造函数,用于通过复制已存在对象的数据创建新对象。

  3. 参数为同类对象的引用:拷贝构造函数的参数是对同类对象的引用,即 const MyClass& other

  4. 对象传递时调用:拷贝构造函数会在对象被传递给函数参数时自动调用。在 main() 函数中,我们使用 MyClass obj2 = obj1; 来复制 obj1,这时拷贝构造函数会被调用。

  5. 默认浅拷贝:拷贝构造函数默认执行浅拷贝,即简单地复制 _data 的值。

  6. 输出信息:在构造函数和拷贝构造函数中,我们打印了相应对象的数据成员 _data 的值。

运行结果

Constructor called for object with data: 10
Object 1: Data: 10
Copy constructor called for object with data: 10
Object 2: Data: 10

 赋值运算符重载

赋值运算符重载是一种特殊的成员函数,用于定义用户自定义类型对象的赋值操作行为。通过重载赋值运算符,可以实现自定义类型对象之间的赋值操作,使其行为类似于内置类型。

赋值运算符重载的概念可以总结为以下几点:

  1. 定义对象赋值行为:赋值运算符重载允许程序员重新定义对象之间的赋值操作。这样,可以通过赋值运算符将一个对象的值赋给另一个对象,从而实现自定义类型对象之间的赋值行为。

  2. 成员函数:赋值运算符重载是一个成员函数,它可以在类的内部进行定义。其名称由关键字 operator 后跟赋值操作符 = 构成。

  3. 返回类型为引用:赋值运算符重载通常返回对象的引用,以支持连续赋值操作。这样可以避免不必要的对象复制。

  4. 参数为同类对象的引用:赋值运算符重载的参数通常是对同类对象的引用。这样可以将右操作数的值复制到左操作数中。

  5. 自赋值检查:在实现赋值运算符重载时,通常需要检查是否为自赋值(即左操作数和右操作数指向同一个对象),以避免出现错误。

  6. 深拷贝:在进行赋值操作时,特别是对于包含动态分配资源的自定义类型,通常需要执行深拷贝,确保资源被正确释放和管理。

赋值运算符重载允许程序员自定义类型的赋值操作,使其更加灵活和符合实际需求。通过适当地重载赋值运算符,可以实现对象之间的赋值行为,从而提高代码的可读性和可维护性。

#include <iostream>
using namespace std;

class Date {
public:
    // 构造函数
    Date(int year = 0, int month = 1, int day = 1) {
        _year = year;
        _month = month;
        _day = day;
    }

    // 赋值运算符重载函数
    Date& operator=(const Date& d) {
        if (this != &d) {
            _year = d._year;
            _month = d._month;
            _day = d._day;
        }
        return *this;
    }

    // 打印函数
    void Print() {
        cout << _year << "年" << _month << "月" << _day << "日" << endl;
    }

private:
    int _year;
    int _month;
    int _day;
};

int main() {
    // 创建日期对象并打印
    Date d1(2024, 5, 8);
    cout << "Original date: ";
    d1.Print();

    // 使用赋值运算符重载将一个日期对象赋值给另一个日期对象
    Date d2;
    d2 = d1;

    // 打印赋值后的日期对象
    cout << "Assigned date: ";
    d2.Print();

    return 0;
}

 重载了赋值运算符 =,使得可以将一个 Date 对象的值赋给另一个 Date 对象。在函数中,我们首先进行自赋值检查,然后将右操作数对象的年、月和日分别赋值给左操作数对象。

const成员

onst修饰类的成员函数
 我们将const修饰的类成员函数称之为const成员函数,const修饰类成员函数,实际修饰的是类成员函数隐含的this指针,表明在该成员函数中不能对this指针指向的对象进行修改。 

例如,我们可以对类成员函数中的打印函数进行const修饰,避免在函数体内不小心修改了对象:

	void Print()const// cosnt修饰的打印函数
	{
		cout << _year << "年" << _month << "月" << _day << "日" << endl;
	}

思考下面几个问题(经典面试题):
1.const对象可以调用非const成员函数吗?
2.非const对象可以调用const成员函数吗?
3.const成员函数内可以调用其他的非const成员函数吗?
4.非cosnt成员函数内可以调用其他的cosnt成员函数吗?

答案是:不可以、可以、不可以、可以
解释如下:
 1.非const成员函数,即成员函数的this指针没有被const所修饰,我们传入一个被const修饰的对象,用没有被const修饰的this指针进行接收,属于权限的放大,函数调用失败。
 2.const成员函数,即成员函数的this指针被const所修饰,我们传入一个没有被const修饰的对象,用被const修饰的this指针进行接收,属于权限的缩小,函数调用成功。
 3.在一个被const所修饰的成员函数中调用其他没有被const所修饰的成员函数,也就是将一个被const修饰的this指针的值赋值给一个没有被const修饰的this指针,属于权限的放大,函数调用失败。
 4.在一个没有被const所修饰的成员函数中调用其他被const所修饰的成员函数,也就是将一个没有被const修饰的this指针的值赋值给一个被const修饰的this指针,属于权限的缩小,函数调用成功。

相关推荐

  1. 对象C++)

    2024-05-16 01:30:04       26 阅读

最近更新

  1. .Net Core WebAPI参数的传递方式

    2024-05-16 01:30:04       0 阅读
  2. QT--气泡框的实现

    2024-05-16 01:30:04       1 阅读
  3. LeetCode 968.监控二叉树 (hard)

    2024-05-16 01:30:04       0 阅读
  4. leetcode热题100.完全平方数(动态规划进阶)

    2024-05-16 01:30:04       0 阅读
  5. leetcode328-Odd Even Linked List

    2024-05-16 01:30:04       0 阅读
  6. C 语言设计模式(结构型)

    2024-05-16 01:30:04       0 阅读
  7. v-if 与 v-show(vue3条件渲染)

    2024-05-16 01:30:04       0 阅读
  8. kafka防止消息丢失配置

    2024-05-16 01:30:04       0 阅读

热门阅读

  1. 调用外部的webservice示例

    2024-05-16 01:30:04       3 阅读
  2. 局域网路由器 交换机 ap模式

    2024-05-16 01:30:04       2 阅读
  3. Spring-Cloud-OpenFeign源码解析-02-OpenFeign自动装配

    2024-05-16 01:30:04       2 阅读
  4. 【鱼眼+普通相机】相机标定

    2024-05-16 01:30:04       3 阅读
  5. FastAdmin菜单规则树形结构分类显示

    2024-05-16 01:30:04       2 阅读
  6. 第十一周学习笔记DAY.1-MySQL

    2024-05-16 01:30:04       2 阅读
  7. mysql 索引失效的原因

    2024-05-16 01:30:04       2 阅读