一 C++编程简介:
1.1 C++历史
(1)C++出现在1983年,当时被叫做C with Class。
(2)历史版本:C++98(1.0)、C++03(TR1,Technical Report 1)、C++11(2.0)、C++14、C++17(2017年刚出)。
(3)C++由C++语言和C++标准库组成。
1.2 C++的class经典分类
(1)C++without pointer members(课程示例Complex)。
(2)C++with pointer members(课程示例String)。
1.3 Object Based 和Object Oriented
(1)Object Based(基于对象)面对的是单一class的设计;
(2)Object Oriented(面向对象,对象导向)面对的是多重class的设计,classes和classes之间的关系。
二 头文件与类的声明
2.1 C++与C的区别
C与C++关于数据与函数的区别:
(1)C有数据和函数,根据数据类型创建具体的数据,函数来处理这些数据,弊端是这些数据是全局的,之后可能会出错;
(2)C++是把数据和处理这些数据的函数封装在一起,叫做class或者struct,C++中class和struct仅有微小区别。
2.2 class经典分类的数据区与函数
(1)C++不带指针的类complex数据区是复数的实部及虚部,成员函数为加、减、乘、除、共轭、正弦等;
(2)C++带指针的类string数据区是字符,其实就是一个指针,指向一串字符,成员函数为拷贝、输出、附加、插入等。
2.3 C++基本代码形式
(1)C++的基本代码形式:类声明(.h)+main函数+类定义(.cpp)+标准库(.h)
(2)C++延伸文件名不一定是.h或者.cpp,也可能是.hpp或者其它甚至无延伸名。
(3)C与C++关于输出的头文件的区别是,C的头文件是stdio.h,C++的头文件是iostream。
2.4 头文件防卫式声明
#ifndef _COMPLEX_
#define _COMPLEX_
、、、
#endif
头文件中要写防卫式声明,这样的作用是:防止由于同一个头文件被包含多次,而导致了重复定义。
2.5 头文件布局
#ifndef _COMPLEX_
#define _COMPLEX_
#include<cmath>
class ostream; //前置声明
class complex;
complex&
_doapl(complex* ths,const complex &r);
class complex //类声明
{
...
};
complex::function... //类定义
#endif
(1)头文件布局除了防卫式声明、包含头文件以外,还包含三个主要部分:前置声明、类的声明、类的定义。
(2)类的声明包含类的头(head)和类的主体(body),有的函数在类的body里面定义,有的在类的body外面定义。
2.6 类模板初体验
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&);
};
void main()
{
complex<double> c1(2.5,1.5);
complex<int> c2(2,6);
...
}
为了不限制类的成员变量的类型,采用模板的形式定义成员变量,等以后需要的用的时候再定义数据类型。
三 构造函数
3.1 inline(内联)函数
class complex
{
public:
complex(double r = 0,double i = 0)
:re (r) ,im ( i )
{ }
complex& operator +=(const complex&);
double real() const { return re;} //自动成为inline候选人
double imag() const { return im;}
private:
double re,im;
friend complex& _doapl (complex*,const complex&);
};
inline double
imag(const complex& x)
{
return x.imag();
}
如果函数在class本体内定义,则它自动变成内联(inline)函数候选人 ,内联函数的效率比较高。若函数较为复杂(例如函数内有循环、递归等)或代码较长,则编译器无法将它变为内联函数。
3.2 类的访问级别
在类的本体内有三种访问级别:public、protect、private。
(1)private:只能由该类中的函数、其友元函数访问,不能被任何其他访问,该类的对象也不能访问。
(2)protected:可以被该类中的函数、子类的函数、以及其友元函数访问,但不能被该类的对象访问。
(3)public:可以被该类中的函数、子类的函数、其友元函数访问,也可以由该类的对象访问。
3.3 构造函数
class complex
{
public:
complex(double r = 0,double i = 0) //构造函数与类名称一致,默认实参
:re (r) ,im ( i ) //初始化列表
{ }
complex& operator +=(const complex&);
...
}
构造函数:创建对象时自动被调用的函数。
(1)和类的名称相同,可以有默认参数,不需要返回类型。
(2)比较好的方法是利用构造函数进行初始化列表,初始化列表在编译前执行,比在构造函数大括号里面赋初值效率高,赋初值在编译后执行。
(3)构造函数可以有很多个,叫做函数的重载。
四 参数传递与返回值
4.1 singleton设计模式
class A{
public:
static A& getInstance();
setup() {...}
private:
A();
A(const A& ths);
...
};
A& A::getInstance()
{
static A a;
return a;
}
构造函数被放在private区表示这个类不能在外界创建对象。
设计模式Singleton:外界只能通过调用getInstance函数才能得到A的对象,不能直接创建A的对象。
4.2 const初体验
class complex
{
...
double real() const {return re;)
double imag() const {return im;)
...
}
void main()
{
const complex c1(2,1);
cout<<c1.real():
cout<<c1.imag();
}
const修饰函数表示外界调用该函数时不会改变里面的数据。使用者创建对象时加const,但是类里面的函数没有用const,会出错!
4.3 参数传递与返回值
(1)参数传递传引用比传值有效率,传引用和传指针一样快(四个字节),但形式比指针漂亮。传引用又不想这个函 数改变传进去的东西,可以在前面用const修饰。
(2)返回值传递尽量传引用,在允许的情况下。返回本地变量,不可以传引用。
4.4 友元函数
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;
};
void main()
{
complex c1(2,1)
complex c2;
c2.func(c1);
}
(1)友元函数可以自由取得friend的private成员,友元打破了c++的封装性。
(2)相同class的各个objects互为友元。
五 操作符重载和临时对象
5.1 this指针
通常在class定义时要用到类型变量自身时,因为这时候还不知道变量名(为了通用也不可能固定实际的变量名), 就用this这样的指针来使用变量自身。
引用链接:http://blog.csdn.net/feiyond/article/details/1652505
5.2 操作符重载——成员函数
inline complex&
_doapl(complex* ths, const complex& r) //do assignment plus
{
ths->re += r.re;
ths->im += r.im;
return *ths;
}
inline complex&
complex::operator += (const complex& r)
{
return _doapl (this,r) ;
}
//编译器是如何看待的如下
/*inline complex&
complex::operator += (this, const complex& r)
{
return _doapl (this,r) ;
}*/
void main()
{
complex c1(2,1);
complex c2(5);
c2 += c1 ;
}
(1)所有的成员函数函数都带有一个隐藏的参数——this指针。
(2)重载+=运算符,返回值是complex&而不是void,如对于:c2 += c1 不需要知道返回值,但是如果对于:c3 += c2 += c1,返回值void就会出错了。
5.3 操作符重载——非成员函数
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 complex& y)
{
return complex (real (x)+ y ,imag (x));
}
inline complex
operator + (const complex& x,const complex& y)
{
return complex ( x +real (y),imag (y));
}
void main()
{
complex c1(2,1);
complex c2;
complex();
complex(4,5);
c2 = c1 + c2; //两个复数相加
c2 = c1 + 5; //复数加实数
c2 = 7 + c1; //实数加复数
cout<<complex(2);
}
若是+重载函数为全局函数,就没法考虑实数+复数和复数+实数的情况,而只能考虑复数加实数的情况。
临时对象 typename():表示创建一个临时对象,作用时间到下一行结束。这些临时对象绝不可return by reference。因为它们是local object。
5.4 小结
(1)构造函数尽量用初始化列表;
(2)函数该不该加const一定要考虑;
(3)参数的传递尽量用引用,要不要加const要考虑;
(4)返回值尽量要用引用,在可以的情况下;
(5)数据尽量放在private,函数尽量放在public;
六 Complex类完整代码:
#ifndef __MYCOMPLEX__
#define __MYCOMPLEX__
class complex;
complex&
__doapl (complex* ths, const complex& r);
complex&
__doami (complex* ths, const complex& r);
complex&
__doaml (complex* ths, const complex& r);
class complex
{
public:
complex (double r = 0, double i = 0): re (r), im (i) { }
complex& operator += (const complex&);
complex& operator -= (const complex&);
complex& operator *= (const complex&);
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 complex& __doami (complex *, const complex&);
friend complex& __doaml (complex *, const complex&);
};
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&
__doami (complex* ths, const complex& r)
{
ths->re -= r.re;
ths->im -= r.im;
return *ths;
}
inline complex&
complex::operator -= (const complex& r)
{
return __doami (this, r);
}
inline complex&
__doaml (complex* ths, const complex& r)
{
double f = ths->re * r.re - ths->im * r.im;
ths->im = ths->re * r.im + ths->im * r.re;
ths->re = f;
return *ths;
}
inline complex&
complex::operator *= (const complex& r)
{
return __doaml (this, r);
}
inline double
imag (const complex& x)
{
return x.imag ();
}
inline double
real (const complex& x)
{
return x.real ();
}
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));
}
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));
}
inline complex
operator * (const complex& x, const complex& y)
{
return complex (real (x) * real (y) - imag (x) * imag (y),
real (x) * imag (y) + imag (x) * real (y));
}
inline complex
operator * (const complex& x, double y)
{
return complex (real (x) * y, imag (x) * y);
}
inline complex
operator * (double x, const complex& y)
{
return complex (x * real (y), x * imag (y));
}
complex
operator / (const complex& x, double y)
{
return complex (real (x) / y, imag (x) / 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;
}
#include <cmath>
inline complex
polar (double r, double t)
{
return complex (r * cos (t), r * sin (t));
}
inline complex
conj (const complex& x)
{
return complex (real (x), -imag (x));
}
inline double
norm (const complex& x)
{
return real (x) * real (x) + imag (x) * imag (x);
}
#endif //__MYCOMPLEX__