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

要是文章有帮助的话

就点赞收藏关注一下啦!

感谢大家的观看

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在面向对象的编程中,C语言并不直接支持和抽象的概念。引用中提到,final关键字用来修饰方法,表示该方法不能在子中被覆盖。而abstract关键字用来修饰抽象方法,表示该方法必须在子中被实现。然而,在C语言中,没有对应的关键字来实现和抽象的概念。 相反,C语言通过结构体来模拟的概念。结构体是一种用户自定义的数据型,可以包含多个不同型的数据成员。通过结构体,我们可以将相关的数据和功能组合在一起。然而,C语言中的结构体不支持继承和多态等面向对象的特性。 在C语言中,我们可以使用函数指针来模拟抽象和接口的概念。函数指针可以指向不同的函数,通过使用函数指针,我们可以实现多态性,即在运行时根据函数指针指向的具体函数来执行不同的操作。 综上所述,C语言并不直接支持面向对象中的和抽象的概念,但可以使用结构体和函数指针来实现似的功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [面向对象——对象](https://blog.csdn.net/shouyeren_st/article/details/126210622)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [面向对象编程原则(06)——依赖倒转原则](https://blog.csdn.net/lfdfhl/article/details/126673771)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IC 1396

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值