C++编程习惯(C++预备篇)

inline 函数

尽量把函数写成inline,这样函数执行会比较快。

  1. 但是并不是所有的编译器都有能力把你写的所有inline函数都真的变成inline函数。你写的inline只能说是对编译器的一个建议而已。
  2. 函数体比较简单的函数尽量写成inline,编译器一般是有能力让它真正变为inline的。

构造函数

  1. 只要是在创建对象,构造函数就会被调用。例如如下三种写法都会调用对应的构造函数:
 (1) complex c1(1, 2);
 (2) complex c2; 
 (3) complex* p = new complex(); 
  1. 使用初始化列而不是在构造函数体{}里去赋初值
// 优雅的写法(建议)
complex(double real = 0, double imag = 0)
	   : re(real), im(imag)
{}

// 可以这么写但是不建议
complex(double real = 0, double imag = 0)
{
   re = real;
   im = imag;
}

一个变量/对象设值分为两个阶段:初始化和赋值。使用初始化列就可以把成员变量赋值在初始化阶段就完成了,不用等到赋值阶段再去做,这样会更快。

  1. 如果一个类里有指针,那么就要配套构造函数和析构函数;如果没有指针,就可以不用析构函数

函数重载

特别说明下 “二义性”。如下构造函数1和构造函数2就不能同时出现,因为构造函数1有默认参数,所以"MyComplex mc1"候选构造函数2的时候发现构造函数1也可以调用。最后编译器就无从选择,只能报错。
(只有在使用"MyComplex mc1"才会报错,只在cpp里写构造函数1和构造函数2是不会报错的)

class MyComplex {
public:
	// 构造函数1
	MyComplex(double r = 0, double i = 0)
		: re(r), im(i)
	{
		cout << "call MyComplex(double r = 0, double i = 0)" << endl;
	}
    // 构造函数2
	MyComplex() : re(0), im(0) 
	{
		cout << "call MyComplex()" << endl;
	}

	void printComplex()
	{
		cout << "re = " << re << ", im = " << im << endl;
	}
private:
	double re;
	double im;
};

int main()
{
	MyComplex mc(1, 2);
	mc.printComplex();
	
	// 因为不知道这里编译器不知道是调用构造函数1还是构造函数2
	MyComplex mc1; // 这里就会报错。如果不写构造函数2,这里就会调用构造函数1

	system("pause");
	return 0;
}

const关键字

对于成员函数,一般分为可以改变成员变量的函数和不可以改变成员变量的函数。对于不会改变成员变量的函数,立马在如下位置加上const,让它变成常量成员函数,也就是说要加的话一定要加上。

class complex
{
public:
	complex(double r = 0, double i = 0):re(r), img(i)
	{}
	double real() const {return re;}
	double imag() const {return img;}

private:
	double re, img;
}; 
  1. 为了合作:定义接口的人,在定义接口时就已经知道这个接口是否需要改变成员变量,假如实现这个接口的人是一个月后才接手的,那么看到这里有const也会明白;
  2. 为了使用者更具有扩展性:
    如果上述成员函数不加const,调用端就只能这么写
complex c1(2, 1);
cout << c1.real();
cout << c1.imag();

如果加const就会报错,比如写成下面这样就会报错。但是使用者就会很奇怪,我这里只是get显示下成员变量而已,为什么不能加const。

// 由于成员函数没加const,使用者的对象加了const就会报错
const complex c1(2, 1);
cout << c1.real();
cout << c1.imag();

写成常量成员函数,使用者上述两种写法都可以。

Value vs Reference

这里有一句很核心的话:传递者无需知道接收者是不是用的引用类型接收。也就是说即使我接收者用的引用类型接收的,你传递者传递的是值也无所谓。

1. 传值与传引用

尽量都传引用,这样效率更快,占用的内存一般也比较小。比如double需要8B,引用就只需要4B,当然如果传char的话,就比引用占用的内存小了。
像double这种【inline void setImg(double& img)】,我们也可以传引用的。

// MyComplex.h
#ifndef __MYCOMPLEX__
#define __MYCOMPLEX__

#include <iostream>
using namespace std;

class MyComplex {
public:
	MyComplex(double r = 0, double i = 0)
		: re(r), im(i)
	{
		cout << "call MyComplex(double r = 0, double i = 0)" << endl;
	}
	
	// double也可以传引用
	inline void setImg(double& img)
	{
		im = img;
	}

    // 保证不能修改传入的引用的值,这里参数加const
	MyComplex& operator += (const MyComplex&);
	
	inline void printComplex()
	{
		cout << "re = " << re << ", im = " << im << endl;
	}
private:
	double re;
	double im;
};

#endif // !__MYCOMPLEX__
// MyComplex.cpp
#include <iostream>
#include "MyComplex.h"

MyComplex& MyComplex::operator += (const MyComplex& cm)
{
	this->im += cm.im;
	this->re += cm.re;
	return *this;
}
#include <iostream>
#include "MyComplex.h"

using namespace std;

int main()
{
	MyComplex m;
	double i = 8;
	m.setImg(i);
	m.printComplex();

	MyComplex mc;
	// mc.setImg(3.2);// 报错。非常量引用的初始值必须是左值。如果setImg()的参数不是&,这里就可以这么用
	system("pause");
	return 0;
}

因为传入的是引用,所以是可以修改这个引用的值的,如果不想修改这个引用的值,则可以加入const修饰,例如MyComplex& operator += (const MyComplex&);

2. 返回"值"与返回"引用"

MyComplex& MyComplex::operator += (const MyComplex& cm)
{
	this->im += cm.im;
	this->re += cm.re;
	return *this;
}

还是这句话:传递者无需知道接收者是不是用的引用类型接收。
这里即使 “return *this”是用值来返回传递,但是也可以用引用"MyComplex& MyComplex::operator += (const MyComplex& cm)"来接收。
这里需要特别注意与后面小结(“2. 作为非成员函数“)的非引用返回值的区别。

当然这里的操作符重载设计成返回值为void也没什么问题,但是解决不了如下连串的+=情况:(n += m += t;)

int main()
{
	MyComplex m;
	double i = 8;
	m.setImg(i);

	MyComplex n;
	n.setImg(8);

	MyComplex t;
	t.setReal(10);

   // 如果设计返回为void就无法解决这种连串+=的情况 
	n += m += t;

	n.printComplex();

	system("pause");
	return 0;
}

浅谈操作符重载

1. 作为成员函数

// MyComplex.h
#ifndef __MYCOMPLEX__
#define __MYCOMPLEX__

#include <iostream>
using namespace std;

class MyComplex {
public:
	MyComplex(double r = 0, double i = 0)
		: re(r), im(i)
	{
		cout << "call MyComplex(double r = 0, double i = 0)" << endl;
	}

	MyComplex& __doapl(MyComplex* , const MyComplex&);
	MyComplex& operator += (const MyComplex&);
private:
	double re;
	double im;
};
#endif // !__MYCOMPLEX__
// MyComplex.cpp
#include <iostream>
#include "MyComplex.h"

MyComplex& MyComplex::operator += (const MyComplex& cm)
{
    // 成员函数这里默认自带this指针
	return __doapl(this, cm);
}

MyComplex& MyComplex::__doapl(MyComplex* ths, const MyComplex& that)
{
	ths->im += that.im;
	ths->re += that.re;
	return *ths;
}

这里的MyComplex& MyComplex::operator += (const MyComplex& cm)等同于MyComplex& MyComplex::operator += (MyComplex*** this**, const MyComplex& cm)但是这个this不能显示的写出来,不同的编译器可能把this放的位置不同,有可能也会放在cm参数后面

2. 作为非成员函数

// MyComplex.h
#ifndef __MYCOMPLEX__
#define __MYCOMPLEX__

#include <iostream>
using namespace std;

class MyComplex {
public:
	MyComplex(double r = 0, double i = 0)
		: re(r), im(i)
	{
		cout << "call MyComplex(double r = 0, double i = 0)" << endl;
	}
	
	inline void printComplex()
	{
		cout << "re = " << re << ", im = " << im << endl;
	}

	inline double getReal() const
	{
		return re;
	}

	inline double getImag() const
	{
		return im;
	}
private:
	double re;
	double im;
};

inline MyComplex operator + (MyComplex& cm, MyComplex& cn)
{
	return MyComplex((cm.getReal() + cn.getReal()), (cm.getImag() + cn.getImag()));
}
inline MyComplex operator + (MyComplex& cm, double c)
{
	return MyComplex(cm.getReal() + c, cm.getImag());
}
inline MyComplex operator + (double c, MyComplex& cm)
{
	return MyComplex(c + cm.getReal(), cm.getImag());
}
#endif // !__MYCOMPLEX__
// Test.cpp
#include <iostream>
#include "MyComplex.h"

using namespace std;


int main()
{
	MyComplex i(1, 2);
	MyComplex j;

	MyComplex test1 = i + j;
	test1.printComplex();// re = 1, im = 2

	MyComplex test2 = i + 10;
	test2.printComplex();// re = 11, im = 2

	MyComplex test3 = 20 + j;
	test3.printComplex();// re = 20, im = 0

	system("pause");
	return 0;
}
  1. 非成员函数没有this指针;
  2. 与成员函数的操作符重载不同,这里的返回值是非引用类型的。
    inline MyComplex operator + (MyComplex& cm, MyComplex& cn)
    因为这个函数的实现里返回值是new出来的临时变量"return MyComplex((cm.getReal() + cn.getReal()), (cm.getImag() + cn.getImag()));",临时变量的生命周期会随着函数的结束而结束,***那么返回的引用让使用者出现异常。***而作为成员函数时,计算的返回值是作用到this的,this一直存在,所以可以返回引用类型。

关于临时变量再补充下面两点:

int main()
{
	// 构造临时变量,都是在分号结束后就结束了
	MyComplex();
	MyComplex(4, 5);

	// 非临时变量,是在main()函数结束后才结束
	MyComplex cm();
	MyComplex cn(4, 5);

	system("pause");
	return 0;
}
#ifndef __MYCOMPLEX__
#define __MYCOMPLEX__

#include <iostream>
using namespace std;

class MyComplex {
public:
	MyComplex(double r = 0, double i = 0)
		: re(r), im(i)
	{
		cout << "call MyComplex(double r = 0, double i = 0)" << endl;
	}
	
	inline void printComplex()
	{
		cout << "re = " << re << ", im = " << im << endl;
	}

private:
	double re;
	double im;
};

inline MyComplex operator + (MyComplex& cm, MyComplex& cn)
{
	return MyComplex((cm.getReal() + cn.getReal()), (cm.getImag() + cn.getImag()));
}

// 这里是重载的正号,不是加号的意思
inline MyComplex operator + (const MyComplex& cm)
{
	return cm;
}
// 这里施重载的负号,不是减法的意思
inline MyComplex operator - (const MyComplex& cm)
{
	return MyComplex(-cm.getReal(), - cm.getImag());
}
#endif // !__MYCOMPLEX__

使用者通过参数就可以区别是调用的 负号/正号重载还是减法/加法重载

int main()
{
	MyComplex cn(4, 5);
	// 负号重载
	(-cn).printComplex();

	system("pause");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值