C++构造函数与析构函数

构造函数

构造函数是一类特殊的类成员函数,在创建类对象的时候被调用。构造函数的名称和类名相同,但通过函数重载,可以创建多个同名的构造函数,条件是每个函数的特征标(参数列表)都不同。另外,构造函数没有声明类型。通常,构造函数用于初始化类对象的成员,初始化应于构造函数的参数列表匹配。

引用《C++ Primer Plus(中文版)第6版》第十章中的一个程序
#include <string>
#include <iostream>

class Stock
{
private:
    std::string company;
    long shares;
    double share_val;
    double total_val;
    void set_tot(){total_val = shares*share_val;}
public:
    Stock();
    Stock(const std::string &co, long n = 0, double pr = 0.0);
    ~Stock();
    void buy(long num, double price);
    void sell(long num, double price);
    void update(double price);
    void show() const;
};

Stock::Stock()
{
    std::cout << "Default constructor called\n";
    company = "no name";
    shares = 0;
    share_val = 0.0;
    total_val = 0.0;
}

Stock::Stock(const std::string &co, long n, double pr)
{
    std::cout << "Constructor using " << co << " called\n";
    company = co;
    if (n < 0)
    {
        std::cout << "Number of shares can't be negative; " << company << "shares set to 0.\n";
        shares = 0;
    }
    else
        shares = n;
    share_val = pr;
    set_tot();
}

Stock::~Stock()
{
    std::cout << "Bye, " << company << "!\n";
}

void Stock::buy(long num, double price)
{
    if (num < 0)
    {
        std::cout << "Number of shares purchased can't be negative. transaction is aborted.\n";
    }
    else
    {
        shares += num;
        share_val = price;
        set_tot();
    }
}

void Stock::sell(long num, double price)
{
    using std::cout;
    if (num < 0)
    {
        cout << "Number of shares sold can't be negative. Transaction is aborted.\n";
    }
    else if (num > shares)
    {
        cout << "You can't sell more than you have! Transaction is aborted.\n";
    }
    else
    {
        shares -= num;
        share_val = price;
        set_tot();
    }
}

void Stock::update(double price)
{
    share_val = price;
    set_tot();
}

void Stock::show() const
{
    using std::cout;
    using std::ios_base;
    ios_base::fmtflags orig = cout.setf(ios_base::fixed, ios_base::floatfield);
    std::streamsize prec = cout.precision(3);
    cout << "Company: " << company << " Shares: " << shares << '\n';
    cout << " Share Price: $" << share_val;
    cout.precision(2);
    cout << " Total Worth: $" << total_val << '\n';
    cout.setf(orig, ios_base::floatfield);
    cout.precision(prec);
}

int main (void)
{
    using std::cout;
    cout << "Using constructors to create new objects\n";
    Stock stock1("NanoSmart");
    stock1.show();
    Stock stock2 = Stock("Boffo Objects", 2, 2.0);
    stock2.show();
    cout << "Assigning stock1 and stock2:\n";
    stock2 = stock1;
    cout << "Listing stock1 and stock2:\n";
    stock1.show();
    stock2.show();
    cout << "Using a constructor to reset an object\n";
    stock1 = Stock("Nifty Foods", 10, 50.0);
    cout << "Revised stock1:\n";
    stock1.show();
    cout << "Done.\n";
    return 0;
}

运行结果如下:


构造函数特点:

构造函数的函数名与类名相同;

构造函数虽然没有返回值,但没有被声明为void类型——实际上,构造函数没有声明类型;

构造函数可以重载(默认构造函数除外);

构造函数的参数表示的不是类成员,而是赋给类成员的值;

定义默认构造函数时要么没有参数,要么要给已有构造函数的所有参数提供默认值;

构造函数还可用于给已有的类对象赋值。



有多种调用构造函数来初始化对象的方式:

Stock stock1("NanoSmart", 12, 20.0);
Stock stock2 = Stock("Boffo Objects", 2, 2.0);
Stock *pstock = new Stock("Apple", 3, 98.0);
//C++11标准中还可以使用列表初始化(将上面三种初始化方式的小括号改为大括号)。

需要注意的是,C++标准允许编译器使用两种方式来执行第二种语法。一种是直接调用构造函数来初始化对象(第一种语法也是如此);另一种是允许调用构造函数来创建一个临时对象,然后将该临时对象复制到stock2中,然后丢弃这个临时对象(编译器可能立刻删除临时对象,也可能会等一段时间)。

在默认情况下,给类对象赋值时,将把一个对象的成员复制给另一个,即将源对象的每个数据成员的内容复制到目标对象中相应的数据成员中。


构造函数不仅仅可用于初始化新对象,还可以为已有对象赋值:

stock1 = Stock("Nifty Foods", 10, 50.0);

使用这种赋值方式,编译器将创建一个临时对象,将该临时对象复制到stock1中。

默认构造函数

默认构造函数是在未提供显式初始值时,用来创建对象的构造函数。即如果没有提供任何构造函数,C++将自动提供默认构造函数,它是默认构造函数的隐式版本,不做任何工作。而且,当且仅当没有定义任何构造函数时,编译器才会提供默认构造函数。为类定义了构造函数后,程序员就必须为它提供默认构造函数。如果提供了非默认构造函数,但没有提供默认构造函数,则下面的声明将出错:

Stock stock1;//由于没有使用构造函数对stock1进行初始化,因此编译器会调用默认构造函数为其初始化

由此可以看出C++标准如此规定的原因可能是想禁止创建未初始化的对象。然而,如果想要创建对象,而不显式地初始化,则必须定义一个不接受任何参数的默认构造函数。定义默认构造函数的方式有两种:

一种是给已有构造函数的所有参数提供默认值:

Stock(const string &co = "Error", int n = 0, double pr = 0.0);

另一种是通过函数重载来定义另一个构造函数——一个没有参数的构造函数:

Stock();

使用了上述任何一种方式创建了默认构造函数后,便可以声明对象变量,而不对它们进行显式初始化。



析构函数

用构造函数创建对象后,程序负责跟踪该对象,直到其过期为止。对象过期时,程序将自动调用一个特殊的成员函数——析构函数,来完成清理工作。和构造函数一样,析构函数也没有返回值和声明类型。与构造函数不同的是,析构函数没有也不能有参数。编译器根据实际情况来决定什么时候调用析构函数。如果创建的是静态存储类对象,则其析构函数将在程序结束时自动被调用。如果创建的是自动存储类对象,则其析构函数将在程序执行完对象声明所在代码块时自动被调用。如果对象是通过new创建的,则它将驻留子栈内存或堆内存,当使用delete来释放内存时,其析构函数将自动被调用。最后,程序可以创建临时对象来完成特定的操作,在这种情况下,程序将在结束对该临时对象的使用时自动调用其析构函数。

由于类对象过期时析构函数将被自动调用,因此必须有一个析构函数。如果没有定义析构函数,编译器将隐式地声明一个默认析构函数,并在发现导致对象被删除的代码后,提供默认析构函数的定义。



本文大部分内容整理自《C++ Primer Plus(中文版)第6版》







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值