目录
头文件和无指针类
以complex类(复数)为例
声明(declaration)
注意撰写规范
防卫式声明(guard)
避免出现引用顺序错乱的问题,避免重复的引用
//complex.h
#ifndef __COMPLEX__ //若未定义“__COMPLEX__”
#define __COMPLEX__ //则定义“__COMPLEX__”
//... 0
//... 1
//... 2
#endif
前置声明 //… 0
(forward declarations)
#include<cmath>
class ostream;
class complex;
//看不懂👇
complex&
__doapl(complex& ths, const complex r);
类-声明 //… 1
(class declarations)
complex.h
*模板
//模板
template<template T>//告诉编译器,T代表一个未定的type
class complex{
public:
//...
private:
//double re,im;//double类型的成员,实部,虚部
T re,im;//模板类型的成员
}
创建对象时,如下使用
complex<double>c1(1.3,4.5);//1.3+i4.5
complex<int>c2(1,4);//1+i4
内联函数
(inline)
很像宏,但有区别。
特点:运算快,仅适用于简单、短小的函数,无法用于结构复杂函数(包含递归、switch等)
标志:在class的body内定义成为inline候选人
(相当于告诉编译器:你尽量将其编译为inline。
class complex{
public:
complex(double r=0,double i=0):re(r),im(i){}//inline
double real() const{return re;}//inline
double imag() const{return im;}//inline
private:
//...
}
inline double//不在本体内定义的,可以加上“inline”关键词
imag(const complex& x){
return x.imag();
}
访问级别
(access level)
class complex{
public://所有函数外界可以调用
complex(double r=0,double i=0):re(r),im(i){}
double real() {return re;}//外界可通过这种方法查看成员,而不能对其做修改
double imag() {return im;}
private://只有类自己可以看到
double re,im;//成员(data)多为private
}
//protected也是一种权限
使用
complex c1(2,1);
cout<<c1.real();//public函数
//cout<<c1.re 错误的
构造函数
(constructor, ctor)
功能
用于创建对象
- 函数名称和类相同
- 参数根据需要设计
- 可以设置默认值,如下(0,0)。
- 没有返回类型(不需要)
- 独有的语法:初值列(initialization list,初始列)。强烈建议使用这一种语法,区分【初始化】与【赋值】,且效率较高。
因为不会考虑在后续程序中”调用构造函数“,而只是用于创建对象,或许也是其与类名必须一致的原因之一。
特殊语法
class complex{
public:
//default argument 默认实参↓
complex(double r=0,double i=0)
:re(r),im(i)//初值列
{}
//{re=r;im=i;}//赋值(assignment)
//...
private:
double re,im;
}
创建一个类对象
complex c1(2,1);//2+i1
complex c2;//0+i0
complex* p=new complex(4);//指向4+i0的指针
一般使用:如果需要创建对象,不可以放在private区域(无法被外界调用)
但设计模式(Singleton)需要把构造函数放在private区:外界仅可用一份
class A{
public:
static A& getInstance();//只能通过这个函数调用A对象
setup(){...}
private:
A();
A(const A& rhs);
//...
};
A& A:getInstance(){
static A a;//这个类自己准备了一份自己的对象a
return a;
}
重载
(overloading)
之所以可以完成函数重载,是因为编译器在编译函数时,会编译
【函数名】、【参数类型】、【参数个数】
class complex{
public:
complex(double r=0,double i=0):re(r),im(i){}//①
complex ():re(0),im(0){}//② 错误的重载
complex (double r):re(r),im(0){}//③ 正确的重载
double real() const{return re;}//
void real(double r) {re=r;}//正确的重载
double imag() const{return im;}
private:
double re,im;
}
①有默认参数,而②无参数。因此不可这样重载。
(不传参时,仍会调用①)
常量成员函数
(const member function)
只读:让外界不能改变数据
class complex{
public:
complex(double r=0,double i=0):re(r),im(i){}
double real() const {return re;}//注意这里的const
double imag() const {return im;}//(只读)
private:
double re,im;
}
const complex c1(2,1);
cout<<c1.real();//无法调用非常量成员函数
如果在声明const对象,就是在创建一个“不能被修改的类对象”,而在调用非常量成员函数时,编译器会将这个信息进行宣告:成员变量可能被修改,与const对象冲突,从而无法调用。
参数传达
(pass by value or pass by reference (to const))
-
尽量不要pass by value:占用空间太大,几个byte就传递几个byte
-
尽量使用pass by reference:传递引用(指针),传地址
-
如果不希望对方对数据进行更改,则加const
class complex{
public:
//pass by value:
complex(double r=0,double i=0):re(r),im(i){}
//pass by reference (to const) 不希望修改
complex& operation +={const complex&};
private:
double re,im;
friend complex& __doapl (complex&, const complex&)
};
ostream&
//pass by reference ↓ (to const)↓
operation << (ostream& os, const complex& s){
//...
}
尽量不要pass by value:占用空间太大,几个byte就传递几个byte
尽量使用pass by reference:传递引用(指针),传地址
complex c1(2,1);
complex c2;
c2 +=c1;//pass by reference,很快
coue<<c2;
返回值传递
(return by value or pass by reference (to const))
同样尽量传递引用
class complex{
public:
complex(double r=0,double i=0):re(r),im(i){}
//↓ return by reference
complex& operation +={const complex&};
//↓ return by value
double real() const {return re;}
double imag() const {return im;}
private:
double re,im;
// ↓ return by reference
friend complex& __doapl (complex&, const complex&)
};
//↓ return by reference
ostream&
operation << (ostream& os, const complex& s){
//...
}
友元
(friend)
功能:可以自由且直接获取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&)
};
inline complex&
__doapl(complex&, const complex&){
ths->re +=r.re;//自由且直接获取complex对象的private成员
ths->im +=r.im;
return *ths;
}
相同类的各个对象互为友元
class complex{
public:
complex(double r=0,double i=0):re(r),im(i){}
// ...
int func(const complex& pa){
return pa.re + pa.im;
}
};
用法:
complex c1(2,1);
complex c2;
c2.func(c1);//通过c2处理c1
析构函数
//后续补充
//通常用于带指针的类
review:类设计的规范性
- 成员放在private
- 使用构造函数的特殊语法进行构造
- 根据需要,限定常量函数(const)
- 根据需要,尽量使用引用(指针)传参/返回值
定义(defination)
complex_test.cpp
#include "complex.h" //自己写的头文件
类-定义 //… 2
(class definition)
-
class body之外的各种函数定义,需要注意是否带上类名
className:: funcName();
tip:注意区分传引用和返回引用的时机。
不可以返回形参的引用:函数结束后,形参将被销毁
class complex{
public:
//... 构造函数
complex& operation +={const complex&};
//... 其他成员函数
private:
double re,im;
friend complex& __doapl (complex&, const complex&)
};
成员函数
className:: funcName(){}
以操作符“+=”的重载为例(成员函数this)
使用this指针
this指针:指向调用者
inline complex&
__doapl(complex* ths,const complex& r){//friend
ths->re+=r.re;//直接对成员操作
ths->im+=r.im;
return *ths;
}
inline complex&
complex::operator +=(const complex& r)
//相当于: (this,const complex& r) ,等价,但不可写this
{
return __doapl(this,r);//方便其他函数的调用,所以包起来
}
使用this指针
complex c1(2,1);
complex c2;
c2+=c1;//c2调用了“+=”函数,是其中的this指针指向的对象
- __doapl:标准库里复数的代码。do assignment plus(赋值的加法)
操作符重载(+=)
return by reference
- 传递者 无需知道 接收者 是以reference的形式接收
inline complex& //返回类型是一个reference
__doapl(complex* ths,const complex& r){
//...
return *ths;//返回指针指向的对象,value
}
inline complex&
complex::operator +=(const complex& r){
return __doapl(this,r);//方便其他函数的调用,所以包起来
}
思考:修改返回类型为void是否可以?
void //修改返回类型
complex::operator +=(const complex& r){
__doapl(this,r);
}
后果:
c2+=c1;//正常使用
c3+=c2+=c1;//如果返回void,将无法使用
非成员函数
不再是成员函数
funcName(){}
inline doble
imag(const complex& x){
return x.imag();
}
inline double
real(const complex& x){
return x.real();
}
complex c1(2,1);
cout<<real(c1);
cout<<c1.real();//相同的输出
操作符重载 : +
(非成员函数无this)以“+”为例
考虑了操作者(client)可能使用这一操作符的种种情形
以重载运算符“+”为例。因为考虑到a+b可以发生在实数之间、复数之间、复数和实数之间,所以采用全局的设计
//c1+c2
inline complex
operation + (const complex& x, const complex& y){
return complex(real(x)+real(y),
imag(x)+imag(y));
}
//c1+5
inline complex
operation + (const complex& x, double r){//加实数
return complex(real(x)+r),
imag(x));
}
//7+c1
inline complex
operation + (double r,const complex& x){//加实数
return complex(real(x)+r),
imag(x));
}
此时不再返回reference,因为返回的必定是local object,是一个加和的结果,而不存在一个具体的对象。
全域函数
操作符重载:<<
global function
inline complex
conj (const complex x)&{
return complex(real(x),-imag(x));//创建临时对象
}
#include<iostream>
ostream&
operator << (ostream& os, const complex){
// ↑不可以加const,每次输入输出都在改变os的状态
return os<<"("<<real(x)<<", "<<imag(x)<<'i)';
}
使用
complex c1(2,1)
cout<<conj(c1);//输出:(2,-1i)
cout<<c1<<conj(c1);//输出:(2,1)(2,-1i)
//cout作为一个对象,被传入为“<<”的os参数