目前为止,我们不能像初始化int,double类型那样初始化类,比如,
int year=1001; //合法的声明
Stock hot={"Auto",200,50.25}; //非法的声明
主要原因是类Stock的数据成员是私有的,只能通过成员函数来访问,因此需要设计合适的成员函数来初始化Stock类。
构造函数
c++提供了一个特殊的成员函数—构造函数,专门用于构造新对象,将值赋给它的数据成员。意思是,c++为这些成员函数提供了名称和语法,而程序员只需要提供方法定义。构造函数的名称与类名相同,例如,构造一个Stock类,它的构造函数可能是名为Stock( )的成员函数。
1.声明和定义构造函数
拿Stock类来举例,创建它的构造函数,声明如下
Stock(const string& co , long n=0, double pr=0.0);
注意:没有返回类型,原型位于类声明的公共部分。
下面是构造函数的一种可能定义:
Stock::Stock(const string& co , long n=0, double pr=0.0)
{
company=co;
if (n<0)
{
std::cout<<"Number of shares can't be nagative: "<<company<<" shares set 0 .\n";
shares=0;
}
else
{
shares=n;
}
share_val=pr;
set_tot();
}
此处的构造函数与上文的acquire()函数功能类似,但是有本质的区别,区别在于程序声明对象时,将自动调用构造函数。
2. 成员名和参数名
如果不熟悉构造函数,可能会将构造函数的参数名选择为成员名,这会造成混乱。参数名不能用类成员相同
为了避免这种混乱,一种常见的做法是:
- 在数据成员名中使用m_前缀;
class Stock
{
private:
std::string m_company;//string类位于名称空间std中
long m_shares;//股票数量
double m_share_val;//每股价格
double m_total_val;//股票总价值
void set_tot(){m_total_val=m_share_val*m_shares;}
....
}
- 在数据成员中使用后缀_;
class Stock
{
private:
std::string company_;//string类位于名称空间std中
long shares_;//股票数量
double share_val_;//每股价格
double total_val_;//股票总价值
void set_tot(){total_val=share_val_*shares_;}
....
}
3.使用构造函数
c++提供了两种使用构造函数来初始化对象的方式。
- 第一种方式:显示地调用构造函数
Stock food=Stock("hello world",200,34.5);
- 第二种方式:隐式的调用构造函数
Stock food("hello world",200,34.5);
- 构造函数与new一起使用的方法
Stock* pf=new Stock("hello world",200,34.5);
4. 默认构造函数
默认构造函数是在未提供显示初始值时,用来创建对象的构造函数。即用于下面这种声明的构造函数:
Stock fluffy_the_cat;
注意:只有程序中没有提供任何构造函数时,c++编译器才会提供默认构造函数,默认构造函数没有参数,声明中不包括值。为类定义了构造函数后,程序员就必须为它提供默认构造函数,如果提供了非默认构造函数,而没有提供默认构造函数,那么 Stock fluffy_the_cat 的声明将会出错。
- 创建默认构造函数的方式之一:给已有构造函数的所有参数提供默认值;
Stock::Stock(const string& co="Error" , long n=0, double pr=0.0);
- 另一种方式是通过函数重载来定义另一个构造函数—一个没有参数的构造函数;
Stock();
注意:由于只能有一个默认构造函数,因此上述两种方式只能任选其一
析构函数
用构造函数创建对象后,程序会跟踪该对象,直到其过期为止。对象过期时,程序将自动调用一个特殊的成员函数—-析构函数。析构函数完成清理工作,比如使用new分配的动态内存,析构函数将使用delete来释放内存,如果构造函数没有使用new,那么实际上析构函数没有什么需要完成的任务。
- 创建析构函数:析构函数的名称很特殊,在类名前加上~,没有返回值和声明类型。
~Stock();
- 定义析构函数:
Stock::~Stock()
{
}
如果程序没有提供析构函数,编译器将隐式的声明一个析构函数,并在对象删除时,提供默认析构函数的定义。
下面是对前文的Stock类的改进:
.h文件
#ifndef STOCK00_H_
#define STOCK00_H_
#include <string>//string类头文件
class Stock
{
private:
std::string m_company;//string类位于名称空间std中
long m_shares;//股票数量
double m_share_val;//每股价格
double m_total_val;//股票总价值
void set_tot(){m_total_val=m_share_val*m_shares;}
public:
//默认构造函数
Stock();
//非默认构造函数
Stock(const std::string& company,long shares,double shares_val);
//析构函数
~Stock();
//void acquire(const std::string& com,long n,double pr);
void buy(long num , double price);
void sell(long num , double price);
void update(double price);
void show()const;
};
#endif
.c文件
#include <iostream>
#include "Stock00.h"
//默认构造函数
Stock::Stock()
{
m_company="Error";
m_shares=0;
m_share_val=0.0;
}
//非默认构造函数
Stock::Stock(const std::string& company,long shares,double shares_val)
{
m_company=company;
if (shares<0)
{
std::cout<<"Number of shares can't be nagative: "<<m_company<<" shares set 0 .\n";
m_shares=0;
}
else
{
m_shares=shares;
}
m_share_val=shares_val;
set_tot();
}
//析构函数
Stock::~Stock()
{
std::cout<<"调用析构函数 \n";
}
获取股票
//void Stock::acquire(const std::string& com,long n,double pr)
//{
// m_company=com;
// if (n<0)
// {
// std::cout<<"Number of shares can't be nagative: "<<m_company<<" shares set 0 .\n";
// m_shares=0;
// }
// else
// {
// m_shares=n;
// }
// m_share_val=pr;
// set_tot();
//}
//增持股票
void Stock::buy(long num , double price)
{
if (num<0)
{
std::cout<<"Number of shares purchased can't be nagative . "<<"\n";
}
else
{
m_shares +=num;
m_share_val=price;
set_tot();
}
}
//卖出股票
void Stock::sell(long num , double price)
{
if (num<0)
{
std::cout<<"Number of shares sold can't be nagative .\n";
}
else
if(num>m_shares)
{
std::cout<<" You can't sell more than you have !\n";
}
else
{
m_shares -=num;
m_share_val=price;
set_tot();
}
}
//更新股票价格
void Stock::update(double price)
{
m_share_val=price;
set_tot();
}
//显示股票信息
void Stock::show()const
{
std::cout<<" company: "<<m_company
<<" shares: "<<m_shares
<<" shares price: $ "<<m_share_val
<<" total price: $ "<<m_total_val<<std::endl;
}
main.cpp
#include <iostream>
#include "Stock00.h"
int main()
{
Stock flutty_the_cat;
const Stock flutty_the_cat2=Stock("hhhh",200,23.4);
flutty_the_cat2.show();
flutty_the_cat.show();
flutty_the_cat.buy(13,18.215);
flutty_the_cat.show();
flutty_the_cat.sell(400,20.00);
flutty_the_cat.show();
flutty_the_cat.buy(300000,40.125);
flutty_the_cat.show();
flutty_the_cat.sell(300000,0.125);
flutty_the_cat.show();
return 0;
}
运行结果:
程序需要注意的几点:
- 在c++11中可以将列表初始化用于类,只要提供与某个构造函数匹配的参数列表的内容,并用大括号将他们括起来即可:
Stock flutty_the_cat2={"hhhh",200,23.4};
- const成员函数
在上例中,void Stock::show()const ,将 const 关键字放在函数的括号后面。当类方法不修改调用对象,就应将其声明为const。就像应该尽可能使用const引用和指针用作函数形参一样。