头文件中的防卫式声明

点击查看代码
#ifndef  __COMPLEX__
#define __COMPLEX__
class complex
{ }
#endif

类的定义

点击查看代码
class complex//class head
{
//class body
//有些函數在此直接定义,另一些在body 之外定义
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&);
};

模板类:

点击查看代码
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&);
};

访问级别

public :外部能看到的

private:只有自己能看到的,一般只有内部函数处理的数据要用private

protect:受保护的

构造函数

构造函数在创建的时候就被调用起来,如果指明,就用指明的参数,如果没有,就要用默认值为参数

点击查看代码
    complex (double r = 0, double i = 0)//0为默认实参(default argument)
: re (r), im (i)//初值列,初始列,initialization list
{ }

初值化是只有构造函数享有的。

如上:re(r),im(i),效果和我们平时在大括号中赋值相同,即re=r,im=i;但是初值化效率更高,请充分利用这种特性。

不带指针的函数多半是不用写析构函数的。

c++容许有多个构造函数,但是上图这种新写的黄色加深的函数是不容许的,因为和最上面的构造函数相冲突,导致我们在构造的时候不知道用哪个构造函数来构造。

函数重载

函数名称一样,但是函数的参数个数或参数类型不一样或有无 const,与返回值类型无关。

单例模式中构造函数可能放到private中

点击查看代码
class A {
public:
static A& getInstance();
setup() { ... }
private:
A();
A(const A& rhs);
...
};
A& A::getInstance()
{
static A a;
return a;
}

如上图,在单例模式中,有一种需求,是把构造函数放到private中的,它不容许外界直接通过A a();来创建。

常量成员函数

当我们在设计接口的时候,这个函数要考虑到是否会对这个函数的返回值作出改变,如果不改变一定要加上const。

如果没加会造成什么后果呢,如果使用者在创建一个复数的对象时,声明这个对象时常量,是不可以被改变的,但是这个函数的内部中没有使用const,这就意味着这个函数有可能会被改变,那么编译器就会报错,使用者就会迷糊,为什么我定义了一个不可以被改变的常量都不能打印出来呢。虽然使用者在使用的时候可能不用const,但是我们要想的更周全。

参数传递

点击查看代码
class complex
{
public:
complex (double r = 0, double i = 0)
: re (r), im (i)
{ }
complex& operator += (const complex&);//pass by reference
double real () const { return re; }
double imag () const { return im; }
private:
double re, im;
friend complex& __doapl (complex*, const complex&);
};

参数传递有两种方式,一种是by Value, 还有一种是by reference

Value 顾名思义就是值,但是当我们的Value大小很大时,假如有几十Kb,也用Value效率很低,很不美观,所以我们可以用by reference的形式来传递,这就是传递的一个指针指向的地址的引用,这个地址中报存的值就是我们想要传进去的东西,这种方式美观大气,如上图c2+=c1,其中传的就是c1指针指向的地址的引用。

传指针的方式很快,如果我们只想传这个数值,但是后续的操作可能会将这个地址中的值改变,然而我们构建这个函数的时候只想让你得到,但是不想让你改怎么办呢,就要加上const

所以在可以的情况下,我们能用by reference就多用by reference,这是一种高效的方法,是我们选择用c++来写程序的原因

返回值传递

同参数传递。

友元

在定义了友元函数后,友元函数可以直接拿到private中定义的数据

点击查看代码
class complex {
...
// 相同class的各个 objects 互为 friends(友员)
int func(const complex &param) {
return param.re + param.im; // 可直接访问 私有数据
}
private:
double re, im;
};

什么样的情况是不可以用by reference来返回的

如果是在函数中创建的对象,那么这个对象就不能用引用的方式来传递,理解为本体已经死掉了,这地址中的东西被回收,就不是我们要看的东西,就不可以传引用。

在之前的博客中有专门一章讲述在C++中为什么尽量在传参时用pass-by-reference取代pass-by-value,其主要目的是为了节省构造函数和析构函数的成本,但这不能完全的取代,假如有下面的一个类:

1 class Rational
2 {
3 public:
4 Rational(int numerator = 0, int denominator = 1);
5 private:
6 int n, d;
7 };

现在为此类添加成员函数operator*,不妨先找找以下的代码有什么问题:

const Rational& operator*(Rational& a, Rational& b)
{
Rational result(a.n*b.n, a.d*b.d);
return result;
}

这段代码有两个问题需要注意:第一 在函数内部定义了一个local对象,然后返回它的地址,而我们应该指导local对象会在退出函数时被销毁,所以如果用户使用这个reference指向的对象会出现”未定义错误“;第二 result的创建还是逃避不了构造函数调用的成本。

总结:

  • 数据一定放在private中
  • 构建函数的写法尽量用初值化写法
  • 参数的传递能用引用就用引用(先考虑by reference)
  • 类名相同时他们互为友元。
  • 不希望改变的值一定要加const
  • 构建函数可以有多个
  • 函数重载只是我们看着名称一样,但是机器看是不同的。

操作符重载

this是这个编译器给我们的一个特殊的指针,这个指针从的是调用者的地址,如c2+=c1,“+=”在编译器眼里相当于一个函数,那么是谁来调用呢,c2,即this指针指向的就是c2。this一般是隐藏起来的,它不能在参数列写出来,但是你可以使用,它可能在参数区前面也可能在参数区后面。 同时我们要保存好习惯,c1我们是不希望改变的,所以要加上const 符号。

1、_doapl这个函数的返回值是一个value ,接收方是complex &,我们在用return by reference的时候传送的一方不需要知道接收者是by reference的方式接收的。

2、我们在设计函数的时候要考虑到连加连减的情况发生,虽然说这个里面参数的value已经发生变化。只是单一的运算是不需要返回值的,但是一旦连续运算,就会出错。

头文件的布局

typename()是临时对象temp object,没有名称,在它的下一行就会被释放吧,如上图所示,我们要传出去的是一个value, 这个complex类型的value就是用的临时对象语法

这里对“<<”进行操作符的重载,首先想到我们传进去的参数是否可以用by reference,好的,可以,这个值会不会被改变,这个参数是我们不想改变的,前面加上const,那么<<是作用到什么上的,作用到cout上,cout是什么,是一个ostream类型的东西,这个东西可不可以传引用,可以,这个东西我们要不要更改,因为每次输出都会改变状态,所以不能加const,它的返回有用吗,没有用,好像可以用来接收,但是如果有多个<<连着输出呢,好的不能用void,我们得返回一个相同类型的对象,这个可以用引用吗,可以。

incline 的使用说明:

incline 意为内联 其目的是提高函数执行效率。在 C程序中,可以用宏代码提高执行效率,内联函数相对于宏来说,增加了对类型的检查,使用起来更安全。宏代码本身不是函数,但使用起来象函数。预处理器用复制宏代码的方式代替函数调用,省去了参数压栈、生成汇编语言的 CALL调用、返回参数、执行return等过程,从而提高了速度。使用宏代码最大的缺点是容易出错,预处理器在复制宏代码时常常产生意想不到的边际效应。对于任何内联函数,编译器在符号表里放入函数的声明(包括名字、参数类型、返回值类型)。如果编译器没有发现内联函数存在错误,那么该函数的代码也被放入符号表里。如果检查函数正确,内联函数的代码就会直接替换函数调用,于是省去了函数调用的开销。

C++ 语言的函数内联机制既具备宏代码的效率,又增加了安全性,而且可以自由操作类的数据成员。所以在C++ 程序中,应该用内联函数取代所有宏代码,“断言assert”恐怕是唯一的例外。assert是仅在Debug版本起作用的宏,它用于检查“不应该”发生的情况。为了不在程序的Debug版本和Release版本引起差别,assert不应该产生任何副作用。如果assert是函数,由于函数调用会引起内存、代码的变动,那么将导致Debug版本与Release版本存在差异。所以assert不是函数,而是宏。

  • 内联可调试
  • 内联可进行安全类型检查或自动类型转换
  • 可访问成员变量
  • 定义在类声明中的成员函数自动转化为内联函数

内联函数的编程风格

关键字inline必须与函数定义体放在一起才能使函数成为内联,仅将inline放在函数声明前面不起任何作用。

//如下风格的函数Foo不能成为内联函数:
inline void Foo(int x, int y); // inline仅与函数声明放在一起
void Foo(int x, int y)
{

}
//而如下风格的函数Foo则成为内联函数:
void Foo(int x, int y);
inline void Foo(int x, int y) // inline与函数定义体放在一起
{

}

所以说,inline是一种“用于实现的关键字”,而不是一种“用于声明的关键字”。一般地,用户可以阅读函数的声明,但是看不到函数的定义。尽管在大多数教科书中内联函数的声明、定义体前面都加了inline关键字,但我认为inline不应该出现在函数的声明中。这个细节虽然不会影响函数的功能,但是体现了高质量C++/C程序设计风格的一个基本原则:声明与定义不可混为一谈,用户没有必要、也不应该知道函数是否需要内联。

适用范围:

1、如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。

2.如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。

点击查看代码
#ifndef _COMPLEX_
#define _COMPLEX_
#include <cmath>
#include <iostream>
using namespace std;
class Complex
{
private:
double re;
double im;
friend Complex& _doapl(Complex*,const Complex& );
friend Complex &__doami(Complex *, const Complex &);
friend Complex &__doaml(Complex *, const Complex &);
public:
Complex (double r = 0, double i = 0)
:re(r),im(i) {} double real() const { return re; }
double imag() const { return im; }
Complex& operator+=(const Complex&c);
Complex& operator-=(const Complex&c);
Complex& operator*=(const Complex&c);
}; inline Complex &
Complex::operator += (const Complex& r){
return _doapl(this,r); //+=的操作符重载交给友元函数做
} inline Complex &
Complex::operator-=(const Complex &r){
return __doami(this, r); //-=的操作符重载交给友元函数做
} inline Complex &
Complex::operator*=(const Complex &r){
return __doaml(this, r); //*=的操作符重载交给友元函数做
} inline Complex&
_doapl(Complex* ths,const Complex& r){ //实现+=的重载
ths->re += r.re;
ths->im += r.im;
return *ths;
}
inline Complex &
__doami(Complex *ths, const Complex &r) //实现-=的重载
{
ths->re -= r.re;
ths->im -= r.im;
return *ths;
} 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 double
real(const Complex &r)
{
return r.real();
} inline double
imag(const Complex &i)
{
return i.imag();
} inline Complex
operator + (const Complex&l,const Complex&r){ //复数加复数
return Complex( real(l) + real(r), imag(l) + imag(r) );
} inline Complex
operator + (const Complex&l, double r){ //复数+实数
return Complex( real(l)+r , imag(l));
}
inline Complex
operator + (double l,const Complex&r){ //实数加复数
return Complex(l+real(r) , imag(r) );
} inline Complex
operator - (const Complex&l,const Complex&r){
return Complex(real(l) - real(r), imag(l) - imag(r) ); //复数-复数
} inline Complex
operator - (const Complex&l, double r){ //复数-实数
return Complex( real(l)-r , imag(l) );
}
inline Complex
operator - (double l,const Complex&r){ //实数-复数
return Complex( l-real(r) , -imag(r) );
} inline Complex
operator*(const Complex &left, const Complex &right)
{
return Complex(real(left) * real(right) - imag(left) * imag(right),
real(left) * imag(right) + imag(left) * real(right));
} inline Complex
operator*(const Complex &left, double right)
{
return Complex(real(left) * right, imag(left) * right);
} inline Complex
operator*(double left, const Complex &right)
{
return Complex(left * real(right), left * imag(right));
} inline Complex
operator/(const Complex &left, double y)
{
return Complex(real(left) / y, imag(left) / 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 &left, const Complex &right)
{
return real(left) == real(right) && imag(left) == imag(right);
} inline bool
operator==(const Complex &left, double right)
{
return real(left) == right && imag(left) == 0;
} inline bool
operator==(double left, const Complex &right)
{
return left == real(right) && 0 == imag(right);
} inline bool
operator!=(const Complex &left, const Complex &right)
{
return real(left) != real(right) || imag(left) != imag(right);
} inline bool
operator!=(const Complex &left, double right)
{
return real(left) != right || imag(left) != 0;
} inline bool
operator!=(double left, const Complex &right)
{
return left != real(right) || 0 == imag(right);
} inline ostream&
operator <<(ostream& o,const Complex& c){
return o<<c.real()<<","<<c.imag();
} #endif

最新文章

  1. linux jexus 服务 设置开机启动
  2. Android 中的code sign
  3. 我的工具箱之Securecrt6.5.0
  4. Crawler4j学习笔记
  5. poj 2599 A funny game 博弈论
  6. 《网络编程》Unix 域套接字
  7. 什么时候需要使用Double? double、float、decimal的区别
  8. HDU5752-Sqrt Bo
  9. 【P2577】 午餐
  10. Leetcode:338. Bit位计数
  11. 正确处理下载文件时HTTP头的编码问题(Content-Disposition)
  12. 移植busybox构建最小根文件系统
  13. requests中get和post传参
  14. C# Thread、ThreadPool、Task、Invoke、BeginInvoke、async、await 汇总
  15. loadrunner&#160;运行场景-场景运行原理
  16. 根据img的url 判断img的图片大小
  17. 线程_synchronized_volatile_ReentranLock
  18. EasyUI相同的Tab只打开一个(即EasyUI方法的调用方法)
  19. Spring学习(五)-----注入bean属性的三种方式( 1: 正常的方式 2: 快捷方式 3: “p” 模式)
  20. select标签中的选项分组

热门文章

  1. Thrift框架-具体使用
  2. centos7 系统级别(持续更新)
  3. Echart可视化学习(九)
  4. v4l2数据获取流程
  5. 若依(ruoyi)代码生成树表结构的那些坑
  6. 《Go组件设计与实现》-netpoll的总结
  7. 《剑指offer》面试题53 - I. 在排序数组中查找数字 I
  8. rocketmq实现延迟队列精确到秒级实现(总结编)
  9. conda : 无法将“conda”项识别为 cmdlet、函数、脚本文件或可运行程序的名称
  10. 使用Xamarin开发移动应用示例——数独游戏(一)项目的创建与调试