目录
三、类的构造函数和折构函数
1、声明和定义构造函数
以前面的方式定义完类后,需要使用acquire函数再给它初始化。而引入类构造函数则可以在创建对象的时候顺便初始化。构造函数的名称和类名相同,传递的参数可以是需要初始化的私有成员的值。以前面的Stock类为例子,需要在定义对象的时候初始化company、shares、shares_val这三个,total_val则可以通过shares、share_val相乘提供。而shares、share_val有时候不需要初始化,则可以通过前面文章函数中的半缺省参数来完成。
//在public下的函数原型
Stock(const std::string & co,long n=0,double pr=0.0);
/*******************************
*函数名:Stock
*作用:构造函数,用于对象创建时初始化
*参数:co:公司名字
n:股票数量
pr:单股价格
*返回:无
*其他:无
********************************/
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相同,区别在于:程序声明对象时,将自动调用构造函数。另外,为了避免有时候成员名和参数重复,可以在数据成员名中使用m_前缀。或者使用后缀_。
2、使用构造函数
C++提供了两种使用构造函数来初始化对象的方式。
Stock food=Stock("World Cabbage",250,1.25);
Stock garment("Furry Mason",50,2.5);
3、默认构造函数
默认构造函数是在未提供显式初始值时,用来创建对象的函数
Stock fluffy_the_cat;
若没有提供任何构造函数,则C++将自动提供默认构造函数。即默认构造函数的隐式版本。默认构造函数没有参数,声明中不包含值。对于Stock类来说,默认构造函数可能如下:
Stock::Stock(){}
需要注意的是,当且仅当没有提供任何构造函数时,编译器才会提供隐式的默认构造函数。若定义了构造函数,则还需要提供默认构造函数,否则只能在创建对象是使用构造函数初始化,而不能直接进行以下操作:
Stock fluffy_the_cat;
定义默认构造函数有两种方式,一种是给已有构造函数的所有参数提供默认值
Stock(const std::string & co="Error",long n=0,double pr=0.0);
另一种是通过函数重载来定义另一个构造函数——一个没有参数的构造函数;
//函数原型
Stock();
//函数定义
Stock::Stock()
{
company="no name";
shares=0;
share_val=0.0;
total_val=0.0;
}
不可以同时使用这两种方法,通常设计类时,应提供对类成员做隐式初始化的默认构造函数。创建默认构造函数后,可以使用以下方式声明对象变量,而不进行显示初始化。
Stock first;
Stock first=Stock();
Stock *prelief=new Stock;
//不过需要注意以下情况
Stock first("Concrete Conglomerate");//调用的是非默认构造函数
Stock second();//声明一个返回Stock对象的函数
4、折构函数
使用构造函数创建对象后,程序会负责跟踪该对象。对象过期时,程序会自动调用一个函数——折构函数完成清理工作。若在构造函数中使用new开辟内存,则折构函数需要使用delete释放内存。若没有使用new,则折构函数实际上没有要完成的任务,不过可以打印一些字符串来查看什么时候调用折构函数。
折构函数名称为:~+类名称。折构函数可以不用声明类型和返回值。不过折构函数没有参数。
//函数原型
Stock();
//函数定义
Stock::~Stock()
{
}
5、改进Stock类
将构造函数和折构函数编写进Stock类
//头文件
#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
删除了acquire()函数(原本的初始化函数)
#include <iostream>
#include "stock00.h"
//源文件
/*******************************
*函数名:Stock
*作用:默认构造函数
*参数:无
*返回:无
*其他:无
********************************/
Stock::Stock()
{
std::cout<<"Default constructor called"<<std::endl;
company="no name";
shares=0;
share_val=0.0;
total_val=0.0;
}
/*******************************
*函数名:Stock
*作用:构造函数,用于对象创建时初始化
*参数:co:公司名字
n:股票数量
pr:单股价格
*返回:无
*其他:无
********************************/
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();
}
/*******************************
*函数名:~Stock
*作用:折构函数
*参数:无
*返回:无
*其他:无
********************************/
Stock::~Stock()
{
std::cout<<"Bye "<<company<<" !\n";
}
/*******************************
*函数名:buy
*作用:购买该公司股票
*参数:num:股票数量
price:单股价格
*返回:无
*其他:无
********************************/
void Stock::buy(long num,double price)
{
if(num<0)
{
std::cout<<"Number of shares purchased can't be negative. "
<<"Transaction is absorted.\n";
}
else
{
shares+=num;
share_val=price;
set_tot();
}
}
/*******************************
*函数名:sell
*作用:售卖股票
*参数:num:股票数量
price:单股价格
*返回:无
*其他:无
********************************/
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 absorted.\n";
}
else
{
shares-=num;
share_val=price;
set_tot();
}
}
/*******************************
*函数名:update
*作用:更新股票价格
*参数:price:单股价格
*返回:无
*其他:无
********************************/
void Stock::update(double price)
{
share_val=price;
set_tot();
}
/*******************************
*函数名:show
*作用:打印数据
*参数:无
*返回:无
*其他:无
********************************/
void Stock::show()
{
using namespace std;
cout<<"Company: "<<company<<endl
<<" Shares:"<<shares<<endl
<<" Share Price:$"<<share_val<<endl
<<" Total Worth:$"<<total_val<<endl;
}
另外,C++11中还可以使用列表初始化
Stock hot_tip={"Derivatives Plus Plus",100,45.0};
Stock jock {"Sport Age Storage,Inc"};
Stock temp{};//注意和前面的返回Stock对象函数的声明区别,这里是大括号,函数是小括号
当类方法不修改调用对象,应将其声明为const成员函数。例如Stock类中的show成员函数函数,格式如下,将const放在括号后
//函数声明
void show() const;
//函数定义
void Stock::show() const
{
}
四、this指针
任以前面的Stock类为例,当我们想对比两只股票的值时,可以定义一个类成员函数
const Stock & topval(const Stock & s) const;
传入一个Stock对象,让调用对象的total_val和传入对象的total_val进行对比,返回股票总值较高的对象。其中第一个const表示返回一个不可以修改的Stock引用,第二个const表示函数不会修改被显式访问的对象,第三个const表示该函数不会修改隐式访问的对象。假如对Stock对象stock1和stock2进行比较,并将股价高的赋给top对象。
top=stock1.topval(stock2);//stock2被显式访问,stock1被隐式访问
top=stock2.topval(stock1);//stock1被显式访问,stock2被隐式访问
则可以在类成员函数中使用如下代码
const Stock & Stock::topval(const Stock & s) const
{
if(s.total_val>total_val)
return s;
else
return *this;
}
当显式访问对象大于隐式访问对象s.total_val>total_val时,返回引用对象s,而当小于或等于时,引入了this指针。this指针指向用来调用成员函数的对象,top=stock1.topval(stock2);将this设置为stock1对象地址。top=stock2.topval(stock1);将this设置为stock2对象地址。所有的类方法都将this指针设置成调用它的对象地址。类成员函数中,total_val只不过是
this->total_val(与指针访问结构体成员一样)的简写。另外返回值使用const将this限定为const,即不可以通过this修改对象的值。而且返回的为*this(返回对象而不是返回对象的地址)。返回类型为引用意味着返回的是调用对象本身而不是副本。
五、对象数组
与结构数组类似,也可以创建对象数组
Stock mystock[4];
Stock stock[4]={
Stock("NanoSmart",12.5,20),
Stock("Boffo",200,2.0),
Stock("Mono",130,3.25),
Stock("Fleep",60,6.5),
};
第一种声明要么未定义任何构造函数,要么定义了构造函数和默认构造函数。每个元素(mystock[0],mystock[1]等)都是Stock对象。
第二种声明使用构造函数初始化数组元素,在这种情况下,需要为每个元素调用构造函数
如果类包括多种构造函数,可以对不同元素调用不同构造函数。
Stock stock[4]={
Stock("NanoSmart",12.5,20),
Stock(),
Stock("Mono",130,3.25),
Stock("Fleep",60,6.5),
};
定义完可以使用Stock方法。
mystock[0].update();
mystock[1].show();
六、类作用域
在类中定义的名称(如类数据成员和类成员函数名)的作用域都是整个类。作用域为整个类的名称只在该类中已知,在类外是不可知的。
1、作用域为类的常量
可以在类中创建一个由所有对象共享的常量。可以通过两种方法实现
①在类中声明一个枚举,这样可以用枚举为整型常量提供作用域为整个类的名称。使用这种方式不会创建类数据成员,所有类对象都不包含枚举。
class Bakery
{
private :
enum {Months=12};
double costs[Months];
}
②使用关键字static,该静态常量与其他静态变量存储在一起,因而只有一个Months,所有Bakery对象共用。
class Bakery
{
private :
static const Months=12;
double costs[Months];
}
2、作用域内枚举
传统的枚举存在一些问题,其中之一是两个枚举定义中的枚举量可能发生冲突。假设有一个处理鸡蛋和T恤的项目,其中可能包含类似下面这样的代码:
enum egg {Small,Medium, Large, Jumbo);
enum t_shirt {Small,Medium, Large,xlarge};
这将无法通过编译,因为 egg Small和 shirt Small 位于相同的作用域内,它们将发生冲突。为避免种问题,C++11 提供了一种新枚举,其枚举量的作用域为类。这种枚举的声明类似于下面这样:
enum class egg {small,Medium, Large, Jumbo);
enum class t_shirt (Small, Medium, Large,xlargel);
也可使用关键字 struct 代替 class。无论使用哪种方式,都需要使用枚举名来限定枚举量.
枚举量的作用域为类后,不同枚举定义中的枚举量就不会发生名称冲突了。
egg choice = egg::Large;
t_shirt Floyd = t shirt::large;
在有些情况下,常规枚举将自动转换为整型,如将其赋给int 变量或用于比较表达式时,但作用域内枚举不能隐式地转换为整型:
enum egg_old{Small, Medium, Large, Jumbol};
enum class t_shirt {Small, Medium, Large,Xlarge};
egg_old one = Medium;
t_shirt rolf = t_shirt::Large;
int king = one;
int ring = rolf;//不允许
if (king < Jumbo)
std::cout << "Jumbo converted to int before comparison.\n";
if (king < t_shirt::Medium)//不允许
std::cout << "Not allowed: < not defined for scoped enum.\n";
但在必要时,可进行强制转换
int Frodo=int(t_shirt::Small);
C++的学习笔记持续更新中~
要是文章有帮助的话
就点赞收藏关注一下啦!
感谢大家的观看
欢迎大家提出问题并指正~