|
本笔记内容均来自于侯捷《面向对象高级编程 C++》
课程可见Youtube,网盘,知乎分享,主要介绍工程项目级C++的应用。
勿在浮沙築高台。 C++语言学习需要区分开语言部分和标准库部分。
头文件与类声明
C++的头文件都会以统一格式书写,以complex.h为例:
#ifndef _COMPLEX_ // 防卫式声明 guard
#define _COMPLEX_
// 0. 前置声明 forward declaration
class ostream;
class complex;
// 1. 类声明 class declaration
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 &);
};
// 2. 类定义 class definition
/* code */
#endif
注意开头结尾不可以省略。
倘若将这个类一般化,例如里面的成员变量变成float和long double,其他的都基本保持一致,则可以使用class template(模板):
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 &);
};
调用这个模板,使用某种数据类型对T进行绑定:
{
complex<double> c1(2.5,1.5);
complex<int> c2(2,6);
}
在类里定义的函数本体,则函数会变成内联函数的候选,例如class complex内的T real()和T imag(),真正是否是内联由编译器决定。
构造函数
构造函数最好使用初始化列表形式写,也可以使用赋值去写,但是不够标准和规范。
class complex
{
public:
complex(double r = 0, double i = 0)
: re(r), im(i)
{ }
private:
double re, im;
friend complex &__doapl(complex *, const complex &);
};
写成这样,要好过于写成以下形式:
class complex
{
public:
complex(double r = 0, double i = 0)
{ re = r; im = i; }
private:
double re, im;
friend complex &__doapl(complex *, const complex &);
};
上面的这种推荐方式(初始化列表)相对而言,使得运行效率更高一点。
函数重载
构造函数可以有很多个重载(overloading),但重载也可以不通过。例如:
class complex
{
public:
// 下面两个构造函数发生了冲突
complex(double r = 0, double i = 0)
: re(r), im(i)
{ }
complex() : re(0),im(0) { }
private:
double re, im;
friend complex &__doapl(complex *, const complex &);
};
int main()
{
complex c1;
complex c2();
}
编译器判定: 类 &#34;complex&#34; 包含多个默认构造函数。
函数后加const,常量成员函数
函数后面加const 表示该函数不会改变数据内容。 函数后面是否加const应该在设计接口时就应该考虑清楚。如果不改变数据内容时,必须要加上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 &);
};
int main()
{
complex c1(2, 1);
cout << c1.real() << endl;
cout << c1.imag() << endl;
}
执行以上程序,输出2,1. 程序将虚数的实部和虚部依次打印出来。 将程序替换成以下,那么:
class complex
{
public:
complex(double r = 0, double i = 0)
: re(r), im(i)
{
}
complex &operator+=(const complex &);
double real() { return re; }
double imag() { return im; }
private:
double re, im;
friend complex &__doapl(complex *, const complex &);
};
int main()
{
const complex c1(2, 1);
cout << c1.real() << endl;
cout << c1.imag() << endl;
}
编译器报错: 对象含有与成员 函数 &#34;complex::imag&#34; 不兼容的类型限定符。
创造一个const对象,将它的实部和虚部打出来,函数 &#34;complex::imag&#34;是无法将常量对象的成员打出来。
引用传参
在传参和求返回值的时候,为了效率提升,尽量使用引用类型。 在特殊情况下,不可以传引用,返回引用:
// 第一參數將會被改動,第二參數不會被改動
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);
}
操作符重载
对复数需要进行数学运算,那么,就会有如下的运算方式:
complex c1(2, 1);
complex c2(4, 0);
// 复数的基础四则运算
cout << c1+c2 << endl;
cout << c1-c2 << endl;
cout << c1*c2 << endl;
cout << c1 / 2 << endl;
// += == != + - 符号的重载
cout << (c1 += c2) << endl;
cout << (c1 == c2) << endl;
cout << (c1 != c2) << endl;
cout << +c2 << endl;
cout << -c2 << endl;
cout << (c2 - 2) << endl;
cout << (5 + c2) << endl;
这时候需要对复数进行重载。
1. 成员函数形式
通用的格式:
class A
{
/* code */
/* 以 + 作为例子 */
A& operate + (A&);
};
inline A&
A::operator + (A& a)
{
/* code */
}
对两个操作符,最终操作形成一个返回值。可以设计成如下的方式:
// do assignment plus
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);
}
传递者无需知道接收者是以 reference 形式接收.
c3 += c2 += c1;
2. 非成员函数形式
为了对应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, 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; // 调用函数1
c2 = c1 + 5; // 调用函数2
c2 = 7 + c1; // 调用函数3
}
以上这三类运算符重载的函数,绝对不剋使用return by reference,因为他们返回值必定是个局部变量。 因为在函数调用结束的时候,局部变量已经销毁,无法传出有效的引用。
// 第一个函数可以返回引用,但是第二个函数一定不可以返回引用
// 因为负号创建了一个临时变量,理由同上
inline complex
operator + (const complex& x)
{
return x;
}
inline complex
operator - (const complex& x)
{
return complex (-real (x), -imag (x));
}
对于<< 和 >> 运算符重载,只能选择非成员函数形式。
// 全局函数,求一个复数的共轭复数
inline complex
conj (const complex& x)
{
return complex (real (x), -imag (x));
}
// ostream 是一个C++标准库的定义对象
#include <iostream.h>
ostream&
operator << (ostream& os, const complex& x)
{
return os << &#39;(&#39; << real (x) << &#39;,&#39;
<< imag (x) << &#39;)&#39;;
}
// 调用函数
int main()
{
complex c1(2,1);
cout << conj(c1);
cout << c1 << conj(c1);
} |
|