C++基础(C++Primer学习)

C++基础(四)

C++的类(接上部分)

思考:之前写的代码风格不好。按照习惯来说,应该把类函数地声明放在类地内部,但是定义放在cpp文件当中,而类地声明则放在头文件当中,这样使得代码看起来更加整齐,结构清晰。
从这一节笔记开始,已经从C++Primer转战C++PrimerPlus了😁。
一、创建一个新的类
1、在头文件中创建一个新的类

class Stock
{
private:
	std::string company;
	long shares;
	double share_val;
	double total_val;
	void set_total()
	{
		total_val = shares * share_val;
	}
public:
	void acquire(std::string& co, long n, double price);
	void buy(long num, double price);
	void sell(long num, double price);
	void update(double price);
	void show();

};

另外,虽然public与private并没有先后顺序要求,但是感觉先写private更好一点,private内部成员代表了类的一些基本属性,这样写可以先从底部结构开始,接口public放在后面。
这里接口成员函数仅仅声明,并不在这里定义。笔记:public成员在类外部的其他代码也能访问,但是private成员仅仅允许类内部成员函数访问或者操作。数据项通常作为私有成员放在private部分
2、在类外部文件定义类成员函数
在类外部定义类成员函数的时候,需要在函数名前使用域解析运算符(::)来标识所属的类:

void Stock::acquire(std::string& co, long n, double price)
{
	;
}

下面再一个stock.cpp文件当中对类成员函数进行定义,注意文件要包含类的头文件。

#include<iostream>
#include"Stock.h"
using namespace std;

void Stock::acquire(const std::string& co, long n, double price)
{
	company = co;
	if (n < 0)
	{
		std::cout << "Number of share can't be negative" << endl;
		shares = 0;
	}
	else
		shares = n;
	share_val = price;
	Stock::set_total();
}

void Stock::buy(long num, double price) //购买股票函数
{
	if (num < 0)
	{
		;// 处理异常
	}
	else
		shares += num;
}

void Stock::sell(long num, double price)
{
	if (num<0)
	{
		;//处理异常
	}
	else if (num > shares)
	{
		;//处理异常 
	}
	elseneil
	{
		shares -= num;
		share_val = price;
		set_total();
	}
}
mei
void Stock::update(double price)
{n
	share_val = price;
	set_total();

}

void Stock::show()
{
	cout << "company:" << company << endl;
	cout <<"Shares:"<< shares << endl;
	cout << "Share_price:" << share_val << endl;
	cout << "Total_worth:" << total_val << endl;
}

其定义位于类内部声明中的将函数都将称为内联函数,类声明中的set_total()函数就是内联函数。类声明通常将短小的成员函数作为内联函数。若想在类外部定义函数但是还要使其成为内联函数,则需要使用inline限定符。

inline函数名;(在类内部)

inline 函数名
{ 
   函数体; 
}(在类外部)

3、类的使用
使用创建好的类,首先要创建一个类的对象,个人理解就是类其实更像是一个模板,我们创建的类的对象具有类的全部特性和功能属性,但是不同的类对象,其不同点在于内部数据可能不同。

Stock num1;
Stock num2;
Stock num3;
num1.acquire("Super", 100, 2.333);
num1.buy(10000, 23.1);
num1.sell(5353, 90);
num1.show();

创建三个对象,对其中第一个对象进行操作演示,调用了类接口的全部函数。

二、类的构造函数与析构函数
类的构造函数在上一节笔记已经提到过了,其作用就是初始化数据成员。如果创建类的时候不自己写构造函数,那么编译器就会使用默认构造函数。但是编译器会报出一个警告:未初始化成员变量
1、定义一个自己定义的构造函数
**构造函数的命名是固定的,需要与类名一致。**在创建类对象时,会自动调用构造函数。构造函数的参数不是表示类成员,而是赋给类成员的值。因此,参数名不能与类成员名相同,为了避免出现这样的问题,常见的作法是在数据成员前加上m_前缀。

private:
	std::string company;
	long m_shares;
	double m_share__val;
	double m_total_val;
	void set_total()
	{
		m_total_val = m_shares * share_val;
	}

更改数据成员名
tips:更改名不要自己去找每个变量然后更改,右键单击变量名选择重命名,VS会自动帮你匹配所有位置的变量然后替换为新名称。
这里将原来的acquire()函数更改为构造函数,就可以在创建类对象的时候就初始化其中的变量,而不用在调用函数初始化。
类外部定义:

Stock::Stock(const std::string& company, long shares, double share_val)
{
	m_company = company;
	m_shares = shares;
	m_share_val = share_val;
}

类内部声明:

class Stock
{
private:
	std::string m_company;
	long m_shares;
	double m_share_val;
	double m_total_val;
	void set_total()
	{
		m_total_val = m_shares * share_val;
	}
public:
	Stock(const std::string& company, long shares, double share_val);
	void buy(long num, double price);
	void sell(long num, double price);
	void update(double price);
	void show();

};

创建好构造函数之后,创建类对象时就要接着输入初始化其参数。

Stock num1("super", 14,6.77);
Stock num2;  //错误。

使用构造函数有两种方式:

Stock num1("super", 14,6.77); //隐式初始化
Stock num2 = Stock("hello", 2324, 9.345); // 显式调用构造函数

更加常用(有逼格)的创建方式,new与构造函数与指针一起使用创建新对象:

Stock* pnum = new Stock("nice", 232, 9.232); 

这条语句创建了一个Stock对象,将其初始化为参数提供的值,并将对象的地址赋给指针,在这种情况下,对象没有名字,但是可以使用指针来管理该对象。
误区:对于非默认构造函数的调用

Stock num1("hhh", 1, 2); //构造函数初始化
Stock num2()//并不表示构造函数,
//而是num2的返回值类型为Stock类。
//如果num2是无参数的构造函数,应该使用下面的声明方式。
Stock num2;

2、析构函数
用构造函数创建对象后,程序负责跟踪该对象,直到其过期为止。对象过期时,程序将自动调用一个特殊的成员函数———析构函数。
析构函数用来完成清理工作,因此实际上十分有用,比如构造函数使用了new来分配内存,那么析构函数则会使用delete来释放内存。由于Stock类没有使用new分配内存,而我们也没有写析构函数(实际上对于上面的Stock类,析构函数什么也不要做),此时编译器会自动给出一个默认的析构函数。
创建析构函数的方式:

~类名(){  函数体;}

啥也不做的析构函数也可以写成内联函数格式,或者不写(因为什么也不做,编译器会提供默认析构函数)。
析构函数什么时候被调用由编译器决定,通常不应该在代码中显示的调用析构函数。