C++学习笔记_6_ 类的成员函数-构造函数

本文详细介绍了C++中的构造函数和析构函数,包括它们的概念、特性、作用及两者之间的区别。构造函数在对象创建时初始化成员变量,确保对象有效状态,而析构函数在对象销毁时释放资源,保证正确销毁。通过构造函数和析构函数,可以确保对象的生命周期得到妥善管理,避免资源泄漏。同时,文章强调了构造函数的重载、默认构造函数以及构造函数链的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

构造函数

对应的析构函数

1.概念

对于以下的日期类,可以通过SetDate公有的方法给对象设置内容。但每次创建对象都调用该方法过于麻烦。
构造函数在对象创建时,就完成了初始化

class Date
{ 
public:
 void SetDate(int year, int month, int day)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 
 void Display()
 {
 cout <<_year<< "-" <<_month << "-"<< _day <<endl;
 }
private:
 int _year;
 int _month;
 int _day;
};
int main()
{
 Date d1,d2;
 d1.SetDate(2021,5,1);
 d1.Display();
 
 Date d2;
 d2.SetDate(2021,7,1);
 d2.Display();
 return 0; }

构造函数是一个特殊成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有一个合适的初始值,并且在对象的生命周期内只调用一次。

2.特性

构造函数是特殊的成员函数,需要注意的是,构造函数的虽然名称叫构造,但是需要注意的是构造函数的主要任务并不是开空间创建对象,而是初始化对象

特性如下:

  1. 函数名类名相同。
  2. 无返回值。
  3. 对象实例化时编译器自动调用对应的构造函数。
  4. 构造函数重载
class Date
{
public :
 // 1.无参构造函数
 Date ()
 {}
 
 // 2.带参构造函数
 Date (int year, int month , int day )
 {
 _year = year ;
 _month = month ;
 _day = day ;
 }
private :
 int _year ;
 int _month ;
 int _day ;
};
void TestDate()
{
 Date d1; // 调用无参构造函数
 Date d2 (2015, 1, 1); // 调用带参的构造函数
 
 // 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
 // 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象
 Date d3(); 
}
  1. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成:
class Date
{
public:
 /*
 // 如果用户显式定义了构造函数,编译器将不再生成
 Date (int year, int month, int day)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 */
private:
 int _year;
 int _month;
 int _day;
};
void Test()
{
 // 没有定义构造函数,对象也可以创建成功,因此此处调用的是编译器生成的默认构造函数
  Date d; 
}
  1. 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认成员函数:
// 默认构造函数
class Date
{ 
public:
 Date()
 {
 _year = 1900 ;
 _month = 1 ;
 _day = 1;
 }
 
 Date (int year = 1900, int month = 1, int day = 1)
 {
 _year = year;
 _month = month;
 _day = day;
 }
private :
 int _year ;
 int _month ;
 int _day ;
};
// 以下测试函数能通过编译吗?
void Test()
{
 Date d1; }
  1. 关于编译器生成的默认成员函数,很多童鞋会有疑惑:在我们不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d对象year/month/_day,依旧是随机值。
  2. 成员变量的命名风格:
// 我们看看这个函数,是不是很僵硬?
class Date
{
public:
 Date(int year)
 {
 // 这里的year到底是成员变量,还是函数形参?
 year = year;
 }
private:
 int year;
};
// 所以我们一般都建议这样
class Date
{
public:
 Date(int year)
 {
 _year = year;
 }
 private:
 int _year;
};
// 或者这样。
class Date
{
public:
 Date(int year)
 {
 m_year = year;
 }
private:
 int m_year;
};

3.作用

构造函数在C++中扮演着非常重要的角色,其主要作用是在创建类的对象时初始化该对象的成员变量。以下是构造函数的详细作用描述:

  • 初始化成员变量:
    构造函数允许程序员为类的实例(对象)的成员变量设置初始值。这些初始值可以是固定的,也可以是基于传递给构造函数的参数来确定的。
  • 确保对象在创建时处于有效状态:
    通过正确地初始化成员变量,构造函数可以确保对象在创建时处于有效的、预期的状态。这有助于防止对象在创建时处于不确定或无效的状态,从而可能导致程序错误或不可预测的行为。
  • 执行必要的设置和分配:
    构造函数可以用于执行任何必要的设置或资源分配,这些设置或资源是对象在其生命周期内所需要的。例如,一个类可能包含指向动态分配内存的指针,构造函数可以负责分配这块内存,并初始化指针。
  • 封装初始化逻辑:
    通过将初始化逻辑封装在构造函数中,可以使得代码更加模块化和可维护。这意味着如果初始化逻辑需要改变,只需要修改构造函数的实现,而不需要修改类的其他部分或使用该类的代码。
  • 支持不同的初始化方式:
    通过重载构造函数(即定义多个具有不同参数列表的构造函数),可以为类的实例提供不同的初始化方式。这使得类更加灵活和易于使用,因为用户可以根据需要选择适当的初始化方式。
  • 实现默认初始化:
    如果没有为类定义任何构造函数,编译器会自动提供一个默认的构造函数(也被称为无参构造函数或默认构造函数)。然而,这个默认构造函数通常不会执行任何初始化操作,除非类的成员变量有默认初始化值。因此,定义自己的默认构造函数可以确保对象在创建时得到适当的初始化。
  • 构造函数链:
    在继承层次结构中,派生类的构造函数可以调用其基类的构造函数,以确保基类部分得到正确的初始化。这种机制被称为构造函数链或构造函数初始化列表。
  • 防止对象被不当地初始化:
    通过将构造函数设为私有或保护,并结合静态成员函数(如工厂方法),可以限制对象的创建方式,从而防止对象被不当地初始化。这在某些设计模式中(如单例模式)非常有用。
  • 异常安全:
    构造函数应该设计为异常安全的。这意味着在构造函数执行过程中发生异常时,应该能够确保对象的状态不会处于部分初始化或无效的状态。这通常通过确保构造函数中的所有操作都是原子的(即要么全部成功,要么全部失败)来实现。

总之,构造函数是C++中用于初始化对象状态的重要工具。通过正确地使用构造函数,可以确保对象在创建时处于有效的、预期的状态,并提供灵活、易于使用的初始化方式。

4.区别

在C++中,构造函数和析构函数是两种特殊的成员函数,它们在对象的生命周期中扮演着不同的角色。以下是构造函数和析构函数的主要区别:

构造函数 (Constructor)

  • 定义:构造函数是特殊的成员函数,用于在创建对象时初始化对象的状态。
  • 调用时机:当创建对象时(无论是通过直接声明、new操作符还是作为类的成员),构造函数会被自动调用。
  • 名称:构造函数的名称与类名相同。
  • 返回类型:构造函数没有返回类型(包括void)。
  • 作用:构造函数用于初始化对象的成员变量,执行必要的资源分配,确保对象在创建时处于有效状态。
class MyClass {  
public:  
    int x;  
  
    // 构造函数  
    MyClass(int value) : x(value) {  
        std::cout << "Object created with x = " << x << std::endl;  
    }  
};  
  
int main() {  
    MyClass obj(10); // 调用构造函数,输出 "Object created with x = 10"  
    return 0;  
}

析构函数 (Destructor)

  • 定义:析构函数也是特殊的成员函数,用于在对象生命周期结束时释放对象所占用的资源。
  • 调用时机:当对象的生命周期结束时(无论是通过作用域结束、delete操作符还是作为类的成员对象被销毁),析构函数会被自动调用。
  • 名称:析构函数的名称是在类名前加上波浪号(~)。
  • 返回类型:析构函数没有返回类型(包括void)。
  • 作用:析构函数用于执行清理操作,如释放动态分配的内存、关闭文件句柄等,确保对象被正确销毁。
class MyClass {  
public:  
    int* ptr;  
  
    // 构造函数  
    MyClass(int value) {  
        ptr = new int(value);  
        std::cout << "Object created with ptr pointing to " << *ptr << std::endl;  
    }  
  
    // 析构函数  
    ~MyClass() {  
        delete ptr;  
        std::cout << "Object destroyed, memory freed" << std::endl;  
    }  
};  
  
int main() {  
    MyClass obj(10); // 调用构造函数  
    // ... 对象的生命周期  
    // 当obj离开作用域时,析构函数被调用  
    return 0;  
} // 输出 "Object destroyed, memory freed"
  • 构造函数在创建对象时初始化对象的状态,确保对象在创建时处于有效状态。
  • 析构函数在对象生命周期结束时释放对象所占用的资源,确保对象被正确销毁。

构造函数和析构函数是C++中面向对象编程的重要概念,它们一起确保了对象的正确创建和销毁,避免了资源泄漏和其他潜在问题。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

明教张公子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值