笔记会持续更新,有错误的地方欢迎指正,谢谢!
这一章是C++ 面向对象的重头戏,以后要转Java/C#的同学这一章要认真地看!
面向对象:把笔这个对象做成一个类,这个类描述了笔的特征,我们可以实例化这个类,从而产生对象(一只只的笔),而笔的使用者不需要关心笔是怎么造出来的,只要会用就好。
类的基本思想是数据抽象和封装:
- 数据抽象: 依赖封装。
- 封装:实现了 接口和实现的分离。用户只能使用接口而无法访问实现部分。
接口:用户所能执行的操作。
实现:包括类的数据成员和函数。
抽象数据类型
设计类的好窍门:你先假装它实现了,去用它,发现需求后,再去实现它。
定义类
1. 成员函数:声明必须在类内部,但定义可在类内或外。
2. this:是指针常量,它只是本身值不变(this是顶层const),但它可以改变它所指对象的值(而如下例子:它所指向的test是常量对象,所以引发了矛盾。)所以不能在常量对象上调用普通成员函数,
例子:
string isbn()
{
return this->bookNo;//this->可省
}
//我们来试试:
const Sales_data test;//test是常量对象。
test.isbn(); //这里会报错,因为在常量对象上调用了普通成员函数。
常量成员函数:参数列表后带const的函数。如此,便将this声明成了指向常量的指针,使得常量对象可访问常量成员函数,但不可写入新值。
例子:
string isbn() const
{
return this->bookNo;//this->可省
}
//我们再来试试:
const Sales_data test;//test是常量对象。
test.isbn(); //正确,因为在常量对象上调用了常量成员函数。
总结:(多想想~反正同理于 const+引用 呗)
1. 常量成员函数–>常量和非常量对象都可调用
2. 普通成员函数–>非常量对象才能调用
补充:在类的外面也可以定义其成员函数
我们来定义一下类中声明的avg_price函数,只要在函数名前面加一个作用域说明符::就行了:
double Sales_data::avg_price() const{}
另外,下面我们再来看一下combine函数的定义:
Sales_data& Sales_data::combine(const Sales_data &rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this; //返回调用该函数的对象
}
上面的&都是引用,不是取地址。 另外,为什么要这样返回呢?
解释:a.combine(b).combine(c)
//a为对象名,1.每次相加都可同时加多项数据;2.为了连续相加呗。
定义类相关的非成员函数
非成员函数:类的设计者常常需要定义一些辅助函数,但它们不属于任何其他的类。
如何判断某函数属于某个类(也就是如何判断一个函数是成员函数):
第一种情况:该函数的定义在该类的大括号内;
第二种情况:该函数的声明在该类的大括号内,而定义(也就是实现)在类外,但实现成员函数时要指明类的名称。
例子:
class A
{
public:
void f1(){}; // 这个就是成员函数。
void f2(); // 这个也是成员函数f2声明,它的实现在类的外部。
};
void A::f2(){} // 这个是成员函数f2的实现。
void f3(){}; // 这个就是非成员函数,它不属于A,也不属于任何其他的类。
构造函数
构造函数的任务是:初始化对象的数据成员,只要类的对象被创建,就会执行构造函数。
注意:构造函数的名字和类名相同且没有返回类型。如果我们的类没有显式地定义构造函数,编译器就会为我们隐式地定义一个默认构造函数。
尽量不用默认构造函数(当然非常简单的类可偷懒用默认初始化),原因如下:
- 因为我们自己才知道我们想把数据成员初始化成什么;
- 有些成员比如指针默认初始化,值未定义会出问题;
- 还有就是类包含类,被包含的那个类没有默认构造函数,那就报错了。
例子:Sales_data(const string &s) : bookNo(s) {}
s是传入参数,s用来初始化成员变量bookNo,这就是个简便写法,等价于:
Sales_data(const string &s)
{
bookNo = s;
}
拷贝、赋值和析构
我们除了要初始化对象(构造函数),还要控制拷贝、赋值和销毁对象(析构函数)时发生的行为。不过别紧张,现在暂时不学,后面章节学。
访问控制与封装
这个太简单,我就不啰嗦了。就是访问修饰符嘛!
C++中struct和class的区别:struct的默认访问权限是public,class是
private,其他没任何区别。
class A //定义一个struct
{
char c1;
};
A a={'p'}; //报错!!!因为class默认为private,对外不可见。而将class改为struct便可。
因此,struct更适合看成是一个数据结构的实现体,class更适合看成是一个对象的实现体。
友元
那问题来了,Sales_data类的数据成员都是private,那类外面的函数read等函数(也就是 非成员函数)就无法编译了,因为它们无法访问成员变量。
解决方法就是让 非成员函数 跟 类 做朋友:友元
友元:允许其他类或函数访问非公有成员。