类是一种将抽象转换为用户定义类型的C++工具,他将数据表示和操纵数据的方法组合成一个整洁的包。
比如有一个股票的类,可以把他的价格,持有人,数量等等,我们可以将股票简化,将操作限制为获得股票、增持、卖出股票、更新股票价格、显示关于股票的信息。此外我们还需要存储一些信息
- 公司名称
- 所持股票数量
- 每股的价格
- 股票总值
这样我们的类大致分为两部分。 - 类声明:以数据成员的方式描述数据部分,以成员函数(被称为方法)的方式描述共有接口
- 类方法定义:描述如何实现类成员函数
接口
接口是一个共享框架。供两个系统交互时使用。比如用户在文档输入一串文字需要用到键盘,需要移动鼠标,计算机接口将用户操作转换为存储在计算机中的具体信息。
对于类,接口是用户调用类的程序,交互系统就是类对象,而接口是编写类的人提供的方法,接口让程序员能够编写与类对象交互的代码,从而让程序能够使用类对象,比如要计算string对象中的字符,可以用方法size,也是接口,如果要使用类,必须了解其公共接口,如果要编写类,必须创建其公共接口。
类
通常C++程序员把接口(类定义)放在头文件当中,并将实现方法(类方法)放在程序源代码当中。
class Stock
{
};
Stock是这个新类的类型名,该声明可以让我们能够声明Stock类型的变量、对象或者实例。每个对象都代表一支股票。
Stock Bill;
Stock white;//表示Bill和white持有某公司股票
我这里直接把书上的例子搬过来。
#include<iostream>
using namespace std;
class Stock
{
private:
string company;
long share;
double share_val;
double total_val;
void set_tot() { total_val = share_val * share; }
public:
void acquire(const string& co, long n, double pr);
void buy(long num, double price);
void sell(long num, double price);
void update(double price);
void show();
};
- company存储了公司的名称
- share存储了持有的股票数量
- share_val存储了每股股票的价格
- total存储了股票总价格
要执行的操作以成员函数形式出现。成员函数可以就地定义,也可以只写声明,在源代码处定义。类的特性是把数据和方法组成一个单元,可以更方便地使用类。
访问控制
private 意为私有的,私生的 成员,只能被本类的成员函数所引用,类外部不能调用(友元类可以,这个以后说明)
public 意为共有的,公开的,公用的 成员,既可以被本类中的成员函数引用,也可以被类的作用域的其他函数所引用,即从类的外部是可以调用的;
protected:意为受保护的成员,不能被类外访问,这点类似private,但是可以被派生类的成员函数访问,有关派生类的说明,以后会讲解。
比如share数据,我们如果要修改它,只能通过Stock的成员函数修改,因为公有成员函数是程序和对象的私有成员之间的桥梁,提供了对象和程序之间的接口。
一般情况下如果不希望外界访问到类中的成员变量,可以设为private,但是必须提供公开的成员函数,如果都设为private,外界函数无法调用,那么我们的数据是无意义的。我想知道知道Bill持有股票的信息,因此在公开的成员函数有show这个成员函数。当然也可以为某些特殊的成员变量设置private但不提供修改接口,这类变量不需要外界使用,只供类使用。还可以把成员函数私有,仅在辅助类内成员函数调用,不对外公开。
这里要说明的是类默认权限是private
实现类成员函数
成员函数特征:
- 定义成员函数 使用::作用域解析运算符标明所属类
- 类方法可以访问private成员
比如我要访问上面类中的update
函数
void Stock::update(double price)
这种表示说明update方法是属于Stock类。其他成员函数在使用update函数时,不需要作用域符号,因为他们都属于一个类的作用域中
关于第二个特征:
可以在show()中这样写
std::cout<<“company = “<<company; //其他私有变量也可以使用
其中的数据成员都是私有,如果用非成员函数访问,编译器不会允许。然后我们下面就去实现类方法,把他写在一个头文件当中stock.h
。
#pragma once
#include<string>
class Stock
{
private:
std::string company;
long share;
double share_val;
double total_val;
void set_tot() { total_val = share_val * share; }
public:
void acquire(const std::string& co, long n, double pr);
void buy(long num, double price);
void sell(long num, double price);
void update(double price);
void show();
};
#include<iostream>
#include"stcok.h"
void Stock::acquire(const std::string& co, long n, double pr)
{
company = co;
if (n<0)
{
std::cout << "Number of shares can't be negative;"
<< company << "share set to 0.\n";
share = 0;
}
else
{
share = n;
share_val = pr;
set_tot();
}
}
void Stock::buy(long num, double price)
{
if (num < 0)
{
std::cout << "Number of shares purchased can't be negative."
<< "Transcation is aborted.\n";
}
else
{
share+= num;
share_val = price;
set_tot();
}
}
void Stock::sell(long num, double price)
{
using std::cout;
if (num<0)
{
cout << "Number if shares sold can't be negative. "
<< "Transcation is aborted.\n";
}
else
{
share -= num;
share_val = price;
set_tot();
}
}
void Stock::update(double price)
{
share_val = price;
set_tot();
}
void Stock::show()
{
std::cout << "Company: " << company
<< "Share: " << share << '\n'
<< "share price: $" << share_val
<< "Total Worth: $" << total_val << '\n';
}
- acpuire()函数管理对某个公司股票的首次购买
- buy() sell()管理增加和减少 股票肯定不能负数 公有函数有利于对私有数据的维护和防护
四个成员函数都修改了total的值 作为辅助函数辅助公有函数,节省了代码量,且如果要修改,工作量也不大。
这里需要说明的是定义位于类声明中的函数会被自动转为内联函数。内联函数就是编译器在编译时,把调用函数替换成了函数代码,减少函数调用开销,适合一些短小的函数。如果不愿意也可以在类声明之外定义内联函数,需要在定义加入inline 如
class Stock
{
private:
...
void hook();
public:
...
}
inline void Stock::hook()
{
...
}
内联函数要求要求在使用他的文件中都有定义,这样内联函数的定义一般在头文件当中
对象的创建
上面我们都没有介绍了类的内部结构,那么如何创建对象。
- 声明类变量
Stock White,Bill;
这样就可以利用对象调用成员函数White.show() Bill.show() 在调用成员函数时,如果函数内部调用了数据成员,那么这个数据成员对应调用他的对象。我们之前学习结构体的时候,我们每个实例化对象都有自己的内存存储空间,类也是一样的,用来存储内部变量和类成员,但是同一个类用的都是同一组类方法,他们将执行同一个代码块,只是代码用到的数据不同。
使用类
C++的目标是使得类和基本类型尽可能相同,我们类的声明和定义都已经编写完成,下面我们通过文件来使用这些接口测试一下:
这里还需要说明一下C++的文件结构,以及这里我们使用到了之前在C语言预编译处理中说到的内容
头文件经常包含的内容
- 函数原型
- 符号常量(#define 和 const)
- 结构声明
- 类声明
- 模板声明
- 内联函数
如果你遇到了
这种情况说明了你的文件之间出现了重定义的问题。简单来说就是有一个头文件被另一个头文件包含,另一个文件又包含了这个头文件,一个头文件被包含了两次,也就是头文件重复包含。而#ifndef 如果编译器没有发现_STOCK_H则执行ifndef中间的代码,下一次再遇到就不会在包含这块代码。就避免了重定义的问题。
网站标题:CV鼻祖洋芋
原创文章,作者:locus,如若转载,请注明出处:https://blog.cvpotato.cn/forward-code/c/196/
本博客所发布的内容,部分为原创文章,转载注明来源,网络转载文章如有侵权请联系站长!