C++进阶(一)

书摘

生活必须被适当的搁置和隐蔽,暴露太多的人会显示脆弱,因其丝毫不知道后退和隐藏,留给彼此余地。有时候,人与人之间只隔了一道墙;有时候,只需要隔一扇门;有时候,只需隔一层花、一株柳的隐约相望,可是,偏偏不能有一丝的接近。

语言联邦

C++最初是在C的基础上夹杂一些面向对象的特性,而这个语言逐渐成熟以后,她变得更加活跃和自由。如今的C++已然支持过程形式、面向对象形式、函数形式、泛型形式、元编程形式,这些能力和弹性使C++成为一个无可匹敌的工具,所以当我们看待C++时,要将其看作一个语言联邦而不是单一的某种语言。

命名

用技术来创造艺术,优雅而又精准的变量、函数、类的命名可以大幅度提升你的程序的可读性。
这里介绍一种: 匈牙利标记法
虽然这个命名方法有许多令人诟病的地方,但是我认为它的优点大于缺点。当然,还有其他的命名方法,可以找一种适合自己的哦。
此处举个小栗子:

/*变量名: 变量的类型首字符加上变量的意义,每个不同单元的首字符大写*/
int iAge = 30// i代表int,Age代表变量的意义是年龄,A大写代表分界。
int iNumber = 4//个数
float fAverageAge = iAge / iNumber;

上面的代码看出问题了吗?没错,两个整型变量相除的结果是7而不是7.5,得到的结果是int型而不是float型。如果没有类型的标记,相信大多数人都会认为这只是一个普通的除法运算吧,标记的好处也体现了出来。 但是当我们要更改fAverageAge为int型时,我们不只是需要修改float为int 还要修改fAverageAge为iAverageAge,这也是它被诟病的地方之一。

及时初始化

在变量的定义时,我们最好直接为其初始化,以免编译器随意赋值带来的弊端。保证对象的初始化在类中的构造函数的参数表中用缺省值完成。
举个例子:

class A
{
public:
	int display()
	{
	return a;
	}
	A(int a = 0): a(a){}
private:
	int a;
};
A m;
int b = A.display();	//b的值是0,证明成功初始化了A::a。

你可能会想,int a;和int a = 0;能有多大的区别呢?确实不大,但是你是否忽略了指针的存在呢?
先看代码:

int iA = 5;
int* ipPoint;
ipPoint = &4;

上面的代码有没有出错的可能性呢?
因为指针被赋予随机值,那么有没有这样一种可能:
在这里插入图片描述
iA存储在最后的四个字节中,那么当ipPointer的位置被随机分到位置1时不会出现问题,因为存储空间足够,如果ipPointer被分配到了位置2呢?会不会破坏iA的数据?会的,此时程序出现了问题,但是编译器不会报错,为了避免这种问题,变量在定义时最好初始化。

减少宏定义出现的频率

尽可能用enum、const、inline替换宏定义。但是要切记,现在的C++并不能做到完全剔除宏定义,该用的时候还是要用。
inline主要是替换宏函数。

#define PI	3.14

宏定义在编译期间会被全部替换掉,也就是说,在编译期间所有的PI都会被替换成3.14,那么我们使用了多少次PI就会占用多少个3.14的存储空间,而且,如果 int a = PI,在某些编译器上的警告会是:隐式类型转换3.14,而不会提示隐士类型转换 PI,如果这个PI使我们自己定义的还好,我们可以找到它,如果是别人在别的文件中定义的呢?你还能找到这个3.14的来源吗?所以,为了避免这种麻烦,采用常量来代替宏定义,如下:

const float PI = 3.14;

这时,无论你调用多少次PI,都只会占用一个3.14的内存,而且出现问题也会提示PI而不是3.14,优势尽显。

类中的常量

先来看一段代码:

class A
{
	private:
	//声明静态常量a。
	static const int a;
};
//定义式
const int A::a = 3;

这个是在类中封装一个静态常量a,注意写法,在类外面定义,在类内的是声明!!
无论如何,这是#define做不到的。

类的构造、析构、赋值运算

赋值

首先我们要了解一下,在类中,编译器为我们创造了哪些默认函数。
默认构造函数、析构函数、默认赋值运算符。
也就是说,只要是同一个类的对象,可以互相赋值。

class A
{
public:
	int display()
	{
	return a;
	}
	A(int a = 0): a(a){}
private:
	int a;
};

A a(5);
//下面的表达是正确的。
A b = a;

但是有时候我们不希望出现赋值运算,那么我们就应该明确的拒绝它,难不成要修改编译器?不不不!
我们只需要把赋值运算符的重载函数作为一个私有成员就行了。

class A
{
public:
	int display()
	{
	return a;
	}
	A(int a = 0): a(a){}
private:
//声明为私有成员。
	A& operater + (A& b);
	int a;
};
A a(5);
//下面的表达是错误的。
A b = a;

这样就可以避免赋值的出现了。

析构函数

如果该类作为基类出现程序,那么最好把析构函数声明为虚函数, 否则在派生类的对象析构时,会出现不知调用哪个析构函数的问题。

class A
{
public:
	A();
	//析构函数 虚函数
	virtual ~A();
private:
//还记得这个吗,复习一下。
	static const int n;
};
class B : public A
{
	/* 省略内容 */
};
//应该还记得吧。
const int n = 5;

构造函数

不要在构造函数和析构函数中调用虚函数!!

总结

大部分篇幅是为了解释清楚为什么这么做,而需要怎么做已经在高亮字体中了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

露忆丶十二

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值