C++面向对象编程
C++面向对象基础知识
类可以分为两类:
- 基于对象(Object Based):面对的是单一的class的设计。类中没有指针
- 面向对象(Object Oriented):面对的是多重class的设计,class和class直接的关系。类中有指针
头文件防卫式声明
#ifndef __COMPLEX__
#define __COMPLEX__
//...
#endif
可以避免程序重复引用头文件导致重复声明定义的错误。
inline
把函数定义为inline
可以让编译器尽可能优化,像宏一样把调用函数替换成函数体的代码,运行会更快。但实际编译器会不会优化,跟函数代码的复杂程度有关。
- 类中定义的函数默认是
inline
类型,编译器经可能做内联 - 在类中声明类外定义的函数需要在类外函数前加
inline
关键字才会被编译器识别为做内联。
class complex {
public:
// 构造函数
complex(double r = 0, double i = 0) : re(r), im(i) {}
complex& operator +=(const complex&);
// 默认 inline 类型,常量成员函数
double real() const {
return re;
}
// 默认 inline 类型,常量成员函数
double imag() const {
return im;
}
// 可以没有析构函数,此class没有指针,不考虑内存空间手动释放
private:
double re;
double im;
friend complex& __doapl(complex*, const complex&);
};
// 定义为 inline 类型
inline double imag(const complex& x) {
return x.imag();
}
构造函数
没有返回值,目的在于构造一个对象。
- 初始化列表(initialization list)
在分配内存空间时进行设初值,这个过程也称为初始化
complex(double r = 0, double i = 0) : re(r), im(i) {}
- 非初始化列表
先分配内存空间,后进行赋值。比初始化列表效率低
complex(double r = 0, double i = 0) {
re = r;
im = i;
}
构造函数重载的歧义
complex(double r = 0, double i = 0) : re(r), im(i) {}
complex() : re(0), im(0) {}
同时定义的话会使构造complex c1
对象时无法确定具体调哪一个构造函数而发生错误。
public or private?
构造函数放在public区域才能被外界调用,外界才能创建对象;单例模式中,构造函数放在private区域。
常量成员函数
告诉函数不会改变数据
double real() const {
return re;
}
若上述real()
函数没有定义为const
类型,在执行下面的代码会报错
const complex c2(1, 2);
cout << c2.real() << endl;
因为const类型的complex对象,通过调用const成员函数访问不可改变的数据才是合理的
值传递,引用传递
对于大块数据的实参形参传递,使用引用相对于值更为高效,引用本质是一个指针,参数传递时就只需要传递指针大小的数据,速度快。
// const complex& 传递引用
complex& operator +=(const complex&);
返回值,返回引用
返回引用更为高效
// complex& 返回引用
complex& operator +=(const complex&);
有元函数
在类中声明某个函数为friend
,则此函数可以访问此类中的所有成员,访问权限和此类的对象一样。同一个类的各个对象互为友元。
class complex {
// ...
private:
// ...
// do assignment plus
friend complex& __doapl(complex*, const complex&);
// ...
};
inline complex& __doapl(complex* ths, const complex& r) {
ths->re += r.re;
ths->im += r.im;
return *ths;
}
操作符重载
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(real(y) + x, imag(y));
}
对+
进行运算符重载,以上代码为不使用this
指针的方法,非成员函数。
而且必须返回值(return by value),不能返回引用(return by reference),因为它们的返回值是局部对象(临时对象),出了函数作用域就消亡了。
inline std::ostream& operator<<(std::ostream& os, const complex& x) {
os << "(" << real(x) << " + " << imag(x) << "i)";
return os;
}
对运算符进行重载可以写在自定义的类中也可以写在类外。但是像<<
操作符,它希望其左侧是ostream
对象,就只能用全局函数的形式进行重载,在形参列表中指明ostream
对象;以自定义类的成员函数进行重载的含义是认定操作符的左侧是调用当前函数的对象,在成员函数里通过this
指针获取调用函数的对象。
而且<<
重载的返回值最好是返回引用,以便cout
连续输出。
complex c1(1, 2);
cout << c1 << ", " << c1 + 7 << endl;
这样以上代码在执行完cout << c1
后就能继续输出后续内容。
class complex
#ifndef __COMPLEX__
#define __COMPLEX__
#include <cmath>
class complex;
complex& __doapl(complex* ths, const complex& r);
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;
double im;
// do assignment plus
friend complex& __doapl(complex*, const complex&);
};
inline double real(const complex& x) {
return x.real();
}
inline double imag(const complex& x) {
return x.imag();
}
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);
}
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(real(y) + x, imag(y));
}
inline complex operator+(const complex& x) {
return x;
}
inline complex operator-(const complex& x) {
return complex(-real(x), -imag(x));
}
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;
}
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;
}
inline complex conj(const complex& x) {
return complex(real(x), -imag(x));
}
#include <iostream>
inline std::ostream& operator<<(std::ostream& os, const complex& x) {
os << "(" << real(x) << " + " << imag(x) << "i)";
return os;
}
#endif