对象和类(进阶)
(1) 类的构造函数和析构函数
1. C++提供了一个特殊的成员函数-类构造函数,专门用于构造新对象、将值赋给它们的数据成员。
构造函数的原型和函数头有一个有趣的特征-虽然没有返回值,但没有被声明为void类型。 实际上,构造函数没有声明类型。
Stock(const std::string & co, long n = 0, double pr = 0.0);
该原型位于类声明的公有部分。
//构造函数定义
Stock::Stock(const std::string & co, long n, double pr)
{
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();
}
上述代码和前面的函数acquire( )相同。 区别在于,程序声明对象外,将自动调用构造函数。
注意:参数名不能与类成员相同。
比如: Stock::Stock(const std::string&company, long shares, double share_val);
是错误的。
1. 使用构造函数。
C++提供了两种使用构造函数来初始化对象的方式。 第一种方式是显式地调用构造函数。
Stock food = Stock("World Cabbage", 250,1.25);
另一种方式是隐式地调用构造函数。
Stockgarment("Flurry Mason", 50, 2.5);
构造函数地使用方式不同于其他类方法。 一般来说,使用对象来调用方法:
stock1.show(); //对象stock1调用show( )方法
但无法使用对象来调用构造函数,因为在构造函数构造出对象之前,对象是不存在地。 因此构造函数用来创建对象,而不能通过对象来调用。
2. 析构函数
用构造函数创建对象后,程序负责跟踪该对象,直到其过期为止。 对象过期时,程序将自动调用一个特殊地成员函数-析构函数。 和构造函数一样,析构函数的名称也很特殊:在类名前加上~。 另外,和构造函数一样,析构函数也可以没有返回值和声明类型。与构造函数不同的是,析构函数没有参数,因此Stock析构函数的原型必须是这样的:
~Stock( );
由于在类对象过期时析构函数将自动被调用,因此必须有一个析构函数。 如果程序员没有提供析构函数,编译器将隐式地声明一个默认析构函数,并在发现导致对象被删除的代码后,提供默认析构函数的定义。
(1) 改进Stock类
1. 头文件:
改进:将构造函数和析构函数原型加入到原来的类声明中;删除了acquire( )函数;使用了#ifndef技术来防止多重包含。
//stock00.h -- 股票类接口,添加了构造函数和析构函数
#ifndef STOCK00_H_
#define STOCK00_H_
#include<string>
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();
}; //请注意结尾处的分号
#endif // !STOCK00_H_
2. 实现文件:
为了让您知道这些方法何时被调用,构造函数和析构函数都显示一条消息。
//stock00.cpp -- 实施股票类
#include<iostream>
#include"stock10.h"
//构造函数定义
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 purchased 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()
{
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);
}
3. 客户文件
//usestock0.cpp -- 客户程序
//和stock10.cpp一起编译
#include<iostream>
#include"stock10.h"
int main()
{
{
using std::cout;
cout << "Uisng constructors to create new objects\n";
Stock stock1("NanoSmart", 12, 20.0); //语法1
stock1.show();
Stock stock2 = Stock("Boffo Objects", 2, 2.0); //语法2
stock2.show();
cout << "Assigning stock1 to 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";
}
std::cin.get();
return 0;
}
(3) 构造函数和析构函数小结
构造函数是一种 特殊的类成员函数,在创建类对象时被调用。 构造函数的名称和类名相同,但通过函数重载,可以创建多个同名的构造函数,条件是每个函数的特征标(参数列表)都不同。另外,构造函数没有声明类型。
就像对象被创建时程序将调用构造函数一样,当对象被删除时,程序将调用析构函数。 每个类都只能有一个析构函数,析构函数没有返回类型,也没有参数,其名称为类名称前加上~。