C++编程习惯(C++预备篇)
Posted Chris_166
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++编程习惯(C++预备篇)相关的知识,希望对你有一定的参考价值。
以下说明的习惯非C++命名习惯,C++命名规范会有专门的总结。
命名规范参考"google-styleguide(Google 开源项目风格指南)"来学习
inline 函数
尽量把函数写成inline,这样函数执行会比较快。
- 但是并不是所有的编译器都有能力把你写的所有inline函数都真的变成inline函数。你写的inline只能说是对编译器的一个建议而已。
- 函数体比较简单的函数尽量写成inline,编译器一般是有能力让它真正变为inline的。
构造函数
- 只要是在创建对象,构造函数就会被调用。例如如下三种写法都会调用对应的构造函数:
(1) complex c1(1, 2);
(2) complex c2;
(3) complex* p = new complex();
- 使用初始化列而不是在构造函数体里去赋初值
// 优雅的写法(建议)
complex(double real = 0, double imag = 0)
: re(real), im(imag)
// 可以这么写但是不建议
complex(double real = 0, double imag = 0)
re = real;
im = imag;
一个变量/对象设值分为两个阶段:初始化和赋值。使用初始化列就可以把成员变量赋值在初始化阶段就完成了,不用等到赋值阶段再去做,这样会更快。
- 如果一个类里有指针,那么就要配套构造函数和析构函数;如果没有指针,就可以不用析构函数
函数重载
特别说明下 “二义性”。如下构造函数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;
;
- 为了合作:定义接口的人,在定义接口时就已经知道这个接口是否需要改变成员变量,假如实现这个接口的人是一个月后才接手的,那么看到这里有const也会明白;
- 为了使用者更具有扩展性:
如果上述成员函数不加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;
- 非成员函数没有this指针;
- 与成员函数的操作符重载不同,这里的返回值是非引用类型的。
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++预备篇)的主要内容,如果未能解决你的问题,请参考以下文章
《C++ Primer Plus》学习笔记 第1章 预备知识