侯捷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

