C++面向对象高级编程(侯捷)笔记1

侯捷C++面向对象高级编程

本文是学习笔记,仅供个人学习使用,如有侵权,请联系删除。

如果你对函数参数何时该传引用(&)、何时该传值,对函数返回值是否传引用(&),对构造函数的初始列的语法,对如何重载操作符operator,对友元函数friend的作用,对函数名称后面加const的意思,对C++标准库中临时对象 typename()的用法,等存在疑惑,那么你就应该学习一下这门课。

学习地址:

Youtube: C++面向对象高级编程(侯捷)

B站: 侯捷C++之C++面向对象高级编程(上)

文章目录

  • 侯捷C++面向对象高级编程
    • 2 头文件和类的声明
    • 3 构造函数
    • 4 参数传递与返回值
    • 5 操作符重载与临时对象
      • 操作符重载1——成员函数
      • 操作符重载2——非成员函数
    • 6 复习Complex类的实现过程

2 头文件和类的声明

头文件的防卫式声明:guard

#ifndef _COMPLEX_

#define _COMPLEX_

#endif

作用:防止重复声明

头文件布局

#ifndef __COMPLEX__
#define __COMPLEX__
// 0. 前置声明
# include
class ostream;
class complex;

complex& __doapl(conplex* ths, const complex& r);

// 1. 类——声明
class complex
{
  ...    
};

// 2. 类——定义
complex::function ...
#endif

不使用模板的复数(complex)类

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&);  // 友元函数
};

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

模板简介:使用模板的复数(complex)类

template<typename 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&);
};

// 使用模板的例子,指定T的具体类型
{
    complex<double> c1(2.5, 1.5);
    complex<int> c2(2, 6);
    ...
}

3 构造函数

inline:函数若在class body内定义完成,便自动成为inline候选人

补充 函数名后面加const的作用:比如上面的 double real() const;

c++ 函数前面和后面 使用const 的作用:

  • 前面使用const 表示返回值为const

  • 后面加 const表示函数不可以修改class的成员

请看这两个函数

  • const int getValue();

  • int getValue2() const;

参考:https://blog.csdn.net/SMF0504/article/details/52311207

构造函数:名称和类名相同,没有返回值类型

// 构造函数不好的写法
class complex
{
 public:
    complex(double r = 0, double i = 0)
    { re = r; im = i;} // 在赋值阶段做,效率差
 private:
    double re, im;
};

// 构造函数好的写法
// 等价于上面
class complex
{
 public:
    complex(double r = 0, double i = 0): re(r), im(i) {}  // 初始列,在初始化阶段做,效率高
 private:
    double re, im;
};

创建对象的例子

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

构造函数的重载(overloading):cpp允许同名函数存在,比如构造函数可以有多个。

构造函数可以被放在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();

4 参数传递与返回值

常量成员函数:在函数名后面加const,表示函数不改变成员内容:

double real() const { return re;}
double imag() const { return im;}

参数传递:

pass by value :传具体的值

pass by reference(to const):传引用

传引用,相当于c语言中的传指针

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&); // 传引用
};

使用举例:

{
    complex c1(2, 1); // 构造函数传参,double类型,传值
    complex c2;
    
    c2 += c1;  // +=运算,传的是引用:const complex&,速度快
    cout << c2;
}

使用const的原因是:使用引用传递,不希望对方对内容进行修改。

不使用const,表示可以对该内容进行修改,比如下面的os参数

ostream&
operator << (ostream& os, const complex& x)
{
    return os << "(" << real(x) << "," << imag(x) << ")";
}

返回值传递

return by value

return by reference(to const)

friend 友元:自由取得friend的private成员

这会打破类的封装性

class complex
{
private:
    double re, im;
    friend complex& __doapl(complex*, const complex&); // 友元函数的声明
};

// 友元函数的定义
inline complex&
__doapl(conplex* ths, const complex& r)
{
    ths->re += r.re;  // 可以直接获取complex的私有成员变量
    ths->im += r.im;
    return *ths;
}

相同class的各个objects互为友元,可以访问到私有数据,打破封装性

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这个复数调用func函数,直接拿到c1这个复数的实部和虚部,并求和
}

什么情况下不能return by reference?

local objects局部对象的引用不要传出去,因为在函数结束的时候,局部变量的生命周期已经结束了。

5 操作符重载与临时对象

操作符重载1——成员函数

do assignment plus是 __doapl函数的含义

inline complex& // 接收端,不用管接收形式,可以是引用,可以是值。这里是引用接收
__doapl(conplex* ths, const complex& r)
{
    ths->re += r.re;  // 可以直接获取complex的私有成员变量
    ths->im += r.im;
    return *ths; // 返回指针指向的内容,object
}

inline complex&
complex::operator += (const complex& r)  // += 操作符重载
{
    return __doapl(this, r);  // this指的是调用者,这是一个指针
}

{
    complex c1(2, 1); 
    complex c2(5);
    
    c2 += c1; // 这里使用 +=,这里的c2就是调用者,就是上面的this,而c1就是上面的r
}

return by reference:传递者无需知道接收者是以什么形式接收。

上面的inline complex& complex::operator += (const complex& r){}函数的返回类型是complex,而不是void,它的考虑是使用者如下的调用方法:

c3 += c2 += c1;

这样就必须有返回值。

操作符重载2——非成员函数

非成员函数,无this指针

class body之外的各种定义

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

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

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

为了对应client的三种可能用法,开发三个函数: 操作符+号的重载

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, const double y)
{
    return complex(real(x) + y, imag(x));
}

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

// 使用
{
    complex c1(2, 1); 
    complex c2;
    
    c2 = c1 + c2;
    c2 = c1 + 5;
    c2 = 7 + c1;
}

上面这三个函数的返回值为什么是值而不是引用呢?

这里务必不能使用return by reference,因为返回的必定是个local object。具体而言,这里计算的是两者之和,它的结果存在局部变量里面,生命周期离开函数就死亡,reference就会指向错误的东西。

临时对象 typename();

临时想要的对象,不想要给它名称,下一行生命周期就结束了。 比如上面的return complex()

正负号重载

取反negate

inline complex // 这里可以改成return by reference
operator + (const complex& x)
{
    return x; // 不是local object
}

inline complex  // 这里务必不能使用return by reference,因为返回的必定是个local object。
operator - (const complex& x)
{
    return complex(-real(x), -imag(x));
}

// 使用
{
    complex c1(2, 1); 
    complex c2;
    cout << -c1;
    cout << +c1;
}

下面是==重载

inline bool
operator == (const complex& x, const complex& y)
{
    return real(x) == real(y) && imag(x) == imag(y);
}

inline bool
operator == (const complex& x, double y)
{
    return real(x) == y && imag(x) == 0;
}

inline bool
operator == (double x, const complex& y)
{
    return x == real(y) && imag(y) == 0;
}

// 使用
{
    complex c1(2, 1); 
    complex c2;
    cout << (c1 == c2);
    cout << (c1 == 2);
    cout << (0 == c2);
}

共轭复数的实现以及 << 操作符的重载

<< 操作符的重载ostream& operator << (ostream& os, const complex& x){}中,为什么传入的参数ostream& os不用加const呢?这是因为output流的状态一直在改变,所以不能用const。

inline complex
conj (const complex& x)
{
    return complex(real(x), -imag(x));
}

#include
ostream&
operator << (ostream& os, const complex& x)
{
    return os << "(" << real(x) << "," << imag(x) << ")";
}

// 使用
{
    complex c1(2, 1);
    cout << conj(c1);  // 输出 (2, -1)
    cout << c1 << conj(c1); // 输出 (2, 1) (2, -1), 连续输出要求 << 返回值还是ostream,不能是void
}

6 复习Complex类的实现过程

整个complex类的实现过程如上面的笔记所示,这里不再展示。另外,complex这个类是class without pointer member(s),后面会将class with pointer member(s),讲的是string类。

你可能感兴趣的:(C++,c++,笔记)