侯捷C++面向对象
学习目标
培养正规的、大气的编程习惯
class
是C++中最重要的,侯捷老师:“一个人写了一个class
,看他是否受过良好的、正规的训练,看以下几点:”
提示
- 数据成员用
private
,成员函数大部分public
- 构造函数使用初始化列表的方式初始化。
initialization list
,并且要写默认值。 - 参数传递是
by value
还是by reference
,同时考虑是否加const
。尽可能的用by reference
- 函数返回值是
return by value
还是return by reference
,同时考虑是否加const
。尽可能的用return by reference
- 在类body内的函数,时刻考虑是否加
const
C++
= C++
语言 + C++
标准库
# 基于对象 vs. 面向对象
Object Based
: 面向的单一的class
的设计。重点:以良好的方式编写C++ class
。class
两种经典分类:class without pointer menber(s)
无指针的complex
(复数类)
class with pointer menber(s)
有指针的string
(字符串类)
Object Oriented
: 面向的是多重class
的设计,class
和class
之间的关系。- 继承
(inheritance)
- 复合
(composition)
- 委托
(delegation)
- 继承
# C vs. C++
# C
- 数据类型(
type
)创建出多个变量(variables
),而处理变量(variables
)的函数(function
)也有多个。
# C++
- 数据类型(
type
)和处理变量(variables
)的函数(function
)放在一个class
中,通过这个class
创建出一个个对象object
,成员变量有多份,但是函数(function
)只有一份,因此函数不属于类对象。(opens new window)
(opens new window)
# 如何规范地编写C++ class
inline
函数,若函数在class body
内定义,便自动能为inline
函数 候选人,函数外要加inline
关键字,但究竟能不能成为inline
函数,由编译器决定。inline
函数像宏一样,有它的特性,但是没有它的缺点,inline
函数执行速度很快。- 构造函数初始化要使用 初始化列表 的方式,不要用
assign
赋值的方式。数值的设定有两个阶段:先是初始化,再是赋值。构造函数可以overloading
,函数重载,但是在编译器看来是不重名的。 class
有带指针的,有不带指针的,不带指针的class
多半不用写析构函数。class
中的函数有两种:改变成员数据的和不改变成员数据的,不改变成员数据的函数要加const
class complex
{
public:
complex(double r = 0, double i = 0): re(r), im(i) { };
double real() const {return re;} //取复数的实部
double imag() const {return im;} //取复数的虚部
private:
double re, im;
};
参数传递尽可能的使用引用(
pass by reference
),不要传值(pass by value
),因为pass by value
是整包传过去,速度慢,而C语言中有指针,可以传首地址过去,加快了速度(指针是4
个字节),C++中多了引用(pass by reference
),拥有指针一样的特性(底层就是指针),但是更漂亮。指针和引用传到另一个函数里,改变值,本体也变了,但是如果传引用只是为了速度更快,不想改变值,加上const
关键字。函数返回值类型同样用引用(return by reference)。传递者无需知道接受者试试以什么样的形式接收。 返回
value
可以用value
接收,也可以用reference
接收。而指针一定要有指针类型去接收。什么情况下可以
return by reference
?
在函数体内计算出的结果,总要有一个变量来接收它,如果这个变量是外部传进来,是在外部定义的,那么可以return by reference
。如果是函数体内定义的局部变量,那么不可以return by reference
,因为执行完这个函数之后,这个变量本身就消失了,怎么能传一个消失的object
回去呢? 引用其实就是一个object
在不同的函数体内有不同的名称,不同函数用不同名称去访问。
return by reference
就是将这个对象本身返回去
reference
作函数返回值类型,作参数传递,这些是很小的细节,但是影响效率。相同
class
的各个object
互为friends
(友元)
class complex
{
public:
complex(double r = 0, double i = 0): re(r), im(i) { };
int func(const complex& param){
return param.re + param.im;
}
private:
double re, im;
};
complex c1(2,1);
complex c2;
c2.func(c1); //c2可以调用c1的成员函数
- 临时对象(
temp object
),语法typename()
。不想给object
名称,下一行生命就结束。
int(1);
complex(4,5);
class
内的成员函数都隐藏着一个参数——this
指针,谁(某一个对象object
)调用该成员函数,this
就是 谁 的地址,this
指针指向调用者。
# 操作符重载
操作符也是一种函数,操作符作用在左边的操作数上,相当于是左边的object
调用了操作数这个函数,那么this
指针就是左边的object
的地址,于是左边的object的地址就会传入到operator +=()
这个函数中。
(opens new window)
(opens new window)
<<
重载不能写成成员函数,因为<<
是cout
调用,而cout
并不属于我们自己所创建的这个类的对象,cout
的类型是ostream
类型,cout
是一个对象,class name
是ostream
。
#include <iostream>
using namespace std;
cout <<......<<......
cout
每一次输出,都在改变cout
的状态,所以是重载时传参进去不能加const
# 编写带指针的class
只要指针成员变量有指针类型,就一定不能用编译器默认的拷贝构造函数、拷贝复制函数和析构函数,要重写。
从使用者的角度,考虑使用者会如何使用,再编写class。
一般而言,对字符串的设计是让string中有一个指针,指针指向字符串的首地址,在需要内存的时候,才创建空间。字符串有大有小,因此不要在string类中放一个数组,不然放多大?
字符串是什么?延续C风格的字符串,有一个指针→字符串的头,后面有一大串,最后有\0
来表示结束。
Big Three