C++的学习之旅——对象和类(下)

目录

三、类的构造函数和折构函数

1、声明和定义构造函数

2、使用构造函数

3、默认构造函数

4、折构函数

5、改进Stock类

四、this指针

五、对象数组

六、类作用域

1、作用域为类的常量

2、作用域内枚举


三、类的构造函数和折构函数

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++的学习笔记持续更新中~

要是文章有帮助的话

就点赞收藏关注一下啦!

感谢大家的观看

欢迎大家提出问题并指正~

猜你喜欢

转载自blog.csdn.net/qq_47134270/article/details/128695790