本文是学习笔记,仅供个人学习使用,如有侵权,请联系删除。
如果你对函数参数何时该传引用(&)、何时该传值,对函数返回值是否传引用(&),对构造函数的初始列的语法,对如何重载操作符operator,对友元函数friend的作用,对函数名称后面加const的意思,对C++标准库中临时对象 typename()的用法,等存在疑惑,那么你就应该学习一下这门课。
学习地址:
Youtube: C++面向对象高级编程(侯捷)
B站: 侯捷C++之C++面向对象高级编程(上)
头文件的防卫式声明: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);
...
}
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();
常量成员函数:在函数名后面加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局部对象的引用不要传出去,因为在函数结束的时候,局部变量的生命周期已经结束了。
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;
这样就必须有返回值。
非成员函数,无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
}
整个complex类的实现过程如上面的笔记所示,这里不再展示。另外,complex这个类是class without pointer member(s),后面会将class with pointer member(s),讲的是string类。