[学习笔记][侯捷]C++学习笔记

简介

     此文档为本人边看视频边记的笔记,方便以后自己回看笔记时能快速想起课程内容,详细内容请看老师讲的视频。

     课程来源:GeekBand,也可以在b站搜到.

如果不能显示出图,请看Github的博文。

C++ 编程简介

基于对象(Object Based)

  • class without pointer members -Complex
  • class with pointer members -String

面向对象(Object Oriented)

  • 学习Classes之间的关系
    • 继承(inheritance)
    • 复合(composition)
    • 委托(delegation)

C++历史

C语言(1972)
C++(1983)早期叫C with Class

C++有很多版本,如 C++98,C++03,C++ 11,C++14

学习C++(其他语言也是)可以分为两个部分:

  • C++语言
  • C++标准库

经典的书:

  • C++ Primer (C++第一个编译器的作者写的)
  • C++ Programming Language (C++之父写的)
  • Effective C++ (专家写的,以条款方式写的)
  • The C++ Standard Library(写的C++的标准库)
  • STL源码剖析(较深入的将标准库)

头文件与类的声明

C vs C++

C vs. C++

C语言数据和函数是分离的,数据都是全局的。这样编程有很大影响。后来发展的C++是数据和函数合在一起成为class,C++的struct几乎等同于class,只有微小的差别。

前面讲过的class可以分为带指针和不带指针的,典型的例子就是complex(不带指针)和string(带指针)

  • Object Based : 面对的是单一class 的设计
  • Object Oriented : 面对的是多重classes 的设计,classes 和classes 之间的关系。

C++的代码基本形式

头文件

注意头文件:<>对应标准库,“”对应自己写的东西

头文件中的防卫式声明(guard)

#ifndef __COMPLEX__
#define __COMPLEX__
...
#endif

可以避免包含头文件时的顺序问题。

头文件的布局

#ifndef __COMPLEX__
#define __COMPLEX__
// forward declarations:前置声明
#include <cmath>
class ostream;
class complex;
complex&
__doapl (complex* ths, const complex& r);
// 类:声明
class complex
{ 
    ...
};
//类:定义
complex::function ...
#endif
class的声明
class complex//class head
{ 
    //class body
    //有些函數在此直接定义,另一些在body 之外定义
public:
    complex (double r = 0, double i = 0)
        : re (r), im (i)
    { }
    complex& operator += (const complex&);//声明
    double real () const { return re; }
    double imag () const { return im; }
private:
    double re, im;
    friend complex& __doapl (complex*, const complex&);
};

使用时

{
    complex c1(2,1);
    complex c2;
    ...
}

上述代码中复数只有double的实部和虚部,为了可以支持多种类型并且不增加类,我们引入模板。

template<typename T>//T也可以是其他字母
class complex
{ 
public:
    complex (T r = 0, T i = 0)
        : re (r), im (i)
    { }
    complex& operator += (const complex&);
    T real () const { return re; }
    T imag () const { return im; }
private:
    T re, im;
    friend complex& __doapl (complex*, const complex&);
};

使用时:

{
    complex<double> c1(2.5,1.5);//T指定为double
    complex<int> c2(2,6);//T指定为int
    ...
}

内敛函数、访问级别、构造函数

inline(内联)函数

class complex
{ 
public:
    complex (double r = 0, double i = 0)
        : re (r), im (i)
    { }
    complex& operator += (const complex&);
    double real () const { return re; }
    double imag () const { return im; }
private:
    double re, im;

    friend complex& __doapl (complex*, const complex&);
};

函数在class本体里定义就形成了inline,inline像宏一样,函数如果是inline的,就会很快,所以所有的函数都写成inline是最好的,然而编译器没有能力把所有函数都做成inline,一般来说程序复杂,编译器就不能做成inline。inline function最后能不能做成inline看编译器的能力。

不在本体里定义inline的要加inline:

inline double
imag(const complex& x)
{
    return x.imag ();
}

访问级别(access level)

  • public :外部能看到的
  • private:只有自己能看到的,一般只有内部函数处理的数据要用private
  • protect:受保护的

错误示范:

{
    complex c1(2,1);
    cout << c1.re;
    cout << c1.im;
}

正确示范:

{
    complex c1(2,1);
    cout << c1.real();
    cout << c1.imag();
}

构造函数(construct,ctor)

如果创建一个对象,构造函数会自动调用起来。没有程序里直接调用函数的写法。
如以下三种方法创建对象:

{
    complex c1(2,1);
    complex c2;
    complex* p = new complex(4);
}

则会自动调用之前写的代码:

    complex (double r = 0, double i = 0)//0为默认实参(default argument)
        : re (r), im (i)//初值列,初始列,initialization  list
    { }

构造函数的写法很独特,他的函数名称一定要和类的名称相同。
构造函数没有返回值类型,因为不需要有,因为构造函数就是要创建类的。
注意默认值并不是构造函数特性,其他函数也可以写成默认值。
initialization list是只有构造函数才有的写法。
如上面的代码也可以写成:

complex (double r = 0, double i = 0)
{ re = r; im = i; }//赋值

两种写法结果是一样的,但是一般有过良好编程习惯的会写成第一种。
第一种效率会好一些,背后有很多原理,简单来讲可以说是因为变量数值的设定有两个阶段,一个是初始化,一个是赋值。第二种写法就是跳过了初始化,效果差。

相对于构造函数有一个析构函数,不带指针的类多半不用写析构函数,如上述所写的Complex。

重载 overloading

构造函数可以有很多个重载。
重载:同名的函数名称有很多个以上。重载常常发生在构造函数身上。

2-3

图中的两个构造函数是不能同时存在的,因为在创建对象时,无法知道应该调用哪个构造函数。

同名的函数在编译器处理后会变成其他名字。这个名字是给机器看的。编译器会把函数的名称,参数有几个,参数是什么类型编码,机器会区分出同名的不同函数。

构造函数也可以放在private里,这是不能按照之前的方法创建对象。详细的使用方法这里先不讲。但要知道有这种写法。
如设计模式Singleton:

class A {
public:
    static A& getInstance();
    setup() { ... }
private:
    A();
    A(const A& rhs);
    ...
};
A& A::getInstance()
{
    static A a;
    return a;
}
A::getInstance().setup();

参数传递与返回

定义函数是确定是否添加const

class complex
{ 
public:
   complex (double r = 0, double i = 0)
   : re (r), im (i)
   { }
   complex& operator += (const complex&);
   double real () const { return re; }
   double imag () const { return im; }
private:
   double re, im;
   friend complex& __doapl (complex*, const complex&);
};

注意

double real () const { return re; }

函数中的const是十分重要的,很多人会忽略。
const在此处的表示函数只是将数据取出,并不改变数据内容。

class的函数可以分为会改变函数内容的和不会改变函数内容的。不会改变数据内容的函数要加加const
开发人员在设计接口时就要确定是否要加const。

如果不加会产生什么后果:

{
   const complex c1(2,1);
   cout << c1.real();
   cout << c1.imag();
}

上面的const说明使用者复数中的2,1是不能变的,假如定义时不加const,说明这个函数可能会改数据,这样在定义和调用就发生了冲突。

参数传递(pass by value vs. pass by reference(to const))

class complex
{ 
public:
    complex (double r = 0, double i = 0)
        : re (r), im (i)
    { }
    complex& operator += (const complex&);//pass by reference
    double real () const { return re; }
    double imag () const { return im; }
private:
    double re, im;
    friend complex& __doapl (complex*, const complex&);
};
  • by value :是数据整包传过去,数据有几个字节就传几个字节
  • by reference(引用):带&,传引用相当与传指针的速度。
  • by reference(to const):没有const时,传给对方的数据,对方可能修改后影响我,如果不想自己受到影响,就要加上const,表示此引用不会该改变数据,对方改变就会出错。

良好的习惯:参数传递尽量的传引用,速度快

返回值传递(return by value vs. return by reference(to const))

上述代码中

complex& operator += (const complex&);//return by reference
double real () const { return re; }//return by value

友元 friend

inline complex&
__doapl (complex* ths, const complex& r)
{
    ths->re += r.re;//自由取得friend 的private成员。
    ths->im += r.im;
    return *ths;
}

相同class的各个object互为friends

4-1

使用场景

看下面的例子do assignment plus(__doapl)

inline complex&
__doapl(complex* ths, const complex& r)
{
    ths->re += r.re;
    ths->im += r.im;
    return *ths;
}
inline complex&
complex::operator += (const complex& r)
{
    return __doapl (this, r);
}

上述代码中的第一个参数(ths)会发生改动,则不能加const,第二个参数®不会发生改动,可以加const。

在函数里面创建的东西不能加引用往外传,因为那是局部的,传到外面就消失了,其他情况都可以传引用(inline complex&)。

操作符重载(operator overloading)

C++中操作符其实就是一种函数,也可以重载。

成员函数重载

所有的成员函数都带有一个隐藏的参数(this),谁调用,谁就是this。this是一个指针,下面是一个复数+=运算的例子。

5-1

传递者无需知道接收者是以reference形式接收,上述代码中编写
return *ths;时不必考虑返回的是以什么形式接收的。

此外,complex::operator +=的返回 complex&,也可以返回void,这时执行,c2+=c1没有问题,但是如果连续赋值c3+=c2+=c1;就会有问题,所以严谨写法为返回complex&。

非成员函数重载(无this)

inline complex
operator + (const complex& x, const complex& y)//复数加复数
{
    return complex (real (x) + real (y),
    imag (x) + imag (y));
}
inline complex
operator + (const complex& x, double y)//复数+实数
{
    return complex (real (x) + y, imag (x));
}
inline complex
operator + (double x, const complex& y)//实数+复数
{
    return complex (x + real (y), imag (y));
}

注意上述代码不能return by reference,因为他们的返回必定是local object。

complex()的语法是,temp object(临时对象), typename();

#include <iostream.h>
ostream&
operator << (ostream& os, const complex& x)
{
    return os << '(' << real (x) << ','
              << imag (x) << ')';
}

);
}
inline complex
operator + (double x, const complex& y)//实数+复数
{
return complex (x + real (y), imag (y));
}

注意上述代码不能return by reference,因为他们的返回必定是local object。

complex()的语法是,temp object(临时对象), typename();

```cpp
#include <iostream.h>
ostream&
operator << (ostream& os, const complex& x)
{
    return os << '(' << real (x) << ','
              << imag (x) << ')';
}

cout是一种对象,ostream.

  • 8
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值