C++面向对象编程(无指针篇)以complex类的实现为例
非常推荐大家学习侯捷老师的C++视频!不论是初学者还是有基础的!
看完几集,侯捷老师C++视频的特点是,纲领性地,有重点地讲解C++语言的要点。和网上其他入门视频不同的是,侯捷老师的C++开始就抛出C++重要的概念,比如面向对象、模板、重载、友元等,对于构建C++的知识体系有极大帮助。适合有一定C语言基础的小伙伴们,缺点是对于初学者比较难理解。
对于初学者来说,虽然在学习过程中很困难,很难理解某些概念,但是只要能理解视频中50%的内容,就已经是非常有收获了。高屋建瓴,直奔主题,在刚入门就能培养“大气”的编程习惯和设计思路,这是优秀的程序员(更是设计者)所必须的。
先上重点:
1、防卫式声明:一定要采用,规范化表达,避免多头文件重定义的麻烦等。
2、类的规范初始化,能用尽量采用。
3、const的使用:C++中,必须严格规范const,需要加上一定要加上。
4、public:公共区域,private:私有区域
建议:数据放在private,函数放在public
但是也有例外:比如单例模式。
5、函数传值和传引用:
建议:尽量传引用,但也存在不能传引用的情况。
6、friend:友元,可以通过friend取得private中的数据
但是必须慎用:因为会破坏C++封装的特性。
相同class的各个objects互为friends。
7、操作符重载:成员函数(this)、非成员函数(无this)
(1)所有的成员函数一定带着一个隐藏的this,不能在参数列写出来,但是this可以用;
(2)有些函数返回一个local object,则不能return by reference,只能return by value。
temp object( 临时对象 ) : typename()
8、class body之外的各种定义(definitions)和使用
9、类与函数的设计注意:
首先,传递参数尽量return by reference;
其次,要考虑操作符的运算对象;
最后,要考虑返回值是否为local object,local object只能return by value。
代码分析
/*complex.h*/
//防卫式声明 guard
#ifndef __COMPLEX__
#define __COMPLEX__
//前置声明 forward declerations
#include <cmath>
class ostream;
class complex;
complex & __doapl(complex *ths, const complex &r);
//template <typename T> //模板类
//类 - 声明 class declerations
class complex //class head
{ //class body 有些函数再次直接定义,另一些在body外定义
public:
complex (double r = 0, double i = 0) //构造函数,函数名和类名相同,r, i为默认实参,没有返回类型
: re(r), im(i) //initialization list 初始化,初值列
{ }
complex &operator += (const complex&);
double real() const {return re;} //函数后的const,不改变数据内容
double imag() const {return im;}
private:
double re, im;
friend complex&__doapl (complex* , const complex&);
};
//类 - 定义 class definition
//VS code 中默认会加上下面的,先不管:
complex::complex(/* args */)
{
}
complex::~complex()
{
}
1、防卫式声明:一定要采用,规范化表达,避免多头文件重定义的麻烦等
/*complex.h*/
//防卫式声明 guard
#ifndef __COMPLEX__
#define __COMPLEX__
/*
内容
*/
#endif
2、规范初始化,能用尽量采用:
public:
complex (double r = 0, double i = 0) //构造函数,函数名和类名相同,r, i为默认实参,没有返回类型
: re(r), im(i) //initialization list 初始化,初值列
下面是不采用初始化的代码:可能会使效率降低,不采用
public:
complex (double r = 0, double i = 0)
{ re(r); im(i); } //赋值语句,放弃了初始化,尽量不采用该方式
3、const的使用:C++中,必须严格规范const,需要加上一定要加上:
double real() const {return re;} //函数后的const,不改变数据内容
double imag() const {return im;}
如果不加const, 以下调用会出错:
const complex c1(2, 1);
cout << c1.real();
cout << c1.imag();
4、public:公共区域,private:私有区域
建议:数据放在private,函数放在public
但是也有例外:比如如下的单例模式:
//ctors构造函数被放在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(); //外界调用
5、函数传值和传引用:
建议:尽量传引用,但也存在不能传引用的情况
//function: pass by value VS pass by reference
class complex
{
public:
complex(double r = 0, double i = 0)
: re(r), im(r)
{ }
complex& operator += (const complex&);
double real() const {return re;}
double imag() const {return im;}
private:
double re, im;
friend complex& __doapl(complex *, const complex&);
};
ostream& operator << (ostream& os, const complex& x)
{
return os << '(' << real(x) << ',' << imag(x) << ')';
}
//临时变量不能返回reference
inline complex& __doapl(complex* ths, const complex& r)
{
ths->re += r.re; //第一参数将会被改动
ths->im += r.im; //第二参数不会被改动
return *ths;
}
inline complex& complex::operater += (const complex& r)
{
return __doapl (this, r);
}
6、friend:友元,可以通过friend取得private中的数据
但是必须慎用:因为会破坏C++封装的特性
相同class的各个objects互为friends
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&);
};
//自由取得friend的private成员
inline complex& __doapl (complex* ths, const complex& r)
{
ths->re += r.re;
ths->im += r.im;
return *ths;
}
//相同class的各个objects互为friends
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的函数处理c1数据,可以
}
7、操作符重载:成员函数(this)、非成员函数(无this)
//operator overloading (操作符重载-1,成员函数) this
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)
/* 以 += 操作符为例
inline complex& complex::operator += (this, const complex& r)
所有的成员函数一定带着一个隐藏的this
不能在参数列写出来,但是this可以用
*/
{
return __doapl(this, r); //__doapl: do assignment plus
}
inline complex& complex::operator += (const complex& r)
{
return __doapl(this, r);
}
/*
传递者无需知道接受者是以reference形式接收 不大理解这句话的意思
*/
/*operator overloading (操作符重载-2,非成员函数) 无this
为了对应client的三种写法,定义三个函数
注意:这些函数都不能return by reference, 因为他们返回的必定是个local object
temp object(临时对象) : typename()
*/
inline complex operator + (const complex& x, const complex& y)
{
return complex(real(x)+real(y), imag(x)+imag(y)); //c2 = c1 + c2
}
inline complex operator + (const complex& x, double y)
{
return complex(real(x) + y, imag(x)); //c2 = c1 + 5
}
inline complex operator + (double x, const complex& y)
{
return complex(x + real(y), imag(y)); //c2 = 7 + c1
}
一元操作符的重载:
inline complex operator + (const complex& x)
{
return x;
}
inline complex operator - (const complex& x)
{
return complex(-real(x), -imag(x));
}
( cout ) << 操作符的重载实现:
inline complex conj(const complex& x)
{
return complex(real(x), -imag(x));
}
#include <iostream>
/* 首先,传递参数尽量return by reference
其次,要考虑操作符的运算对象
最后,要考虑返回值是否为local object,local object只能return by value
*/
ostream& operator << (ostream& os, const complex& x)
{
return os << '(' << real(x) << ',' << imag(x) << ')';
}
{
complex c1(2, 1);
cout << conj(c1);
cout << c1 << conj(c1);
}
9、class body之外的定义(definitions)和使用
//class body之外的各种定义(definitions)
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);
}