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

Posted Chris_166

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++编程习惯(C++预备篇)相关的知识,希望对你有一定的参考价值。

以下说明的习惯非C++命名习惯,C++命名规范会有专门的总结。
命名规范参考"google-styleguide(Google 开源项目风格指南)"来学习

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)"来接收。

当然这里的操作符重载设计成返回值为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()));",临时变量的生命周期会随着函数的结束而结束。那么返回的引用让使用者出现异常。

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

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;

以上是关于C++编程习惯(C++预备篇)的主要内容,如果未能解决你的问题,请参考以下文章

1.预备知识

C++学习笔记—— 基础知识预备,以及“面向对象”的C++

《C++ Primer Plus》学习笔记 第1章 预备知识

#yyds干货盘点#Android C++系列:Linux Socket编程预备知识

漫谈C++:良好的编程习惯与编程要点

C++ 编程习惯与编程要点