| 
 | 
 
本笔记内容均来自于侯捷《面向对象高级编程 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); 
} |   
 
 
 
 |