改善c++程序的150个建议(读后总结)-------12-18

12.优先使用前置操作符

#include <iostream>
using namespace std;
class A
{
private:
		int num;
public:
		A operator++();          //前置++
		A operator++(int);      //后置++
};
A A::operator++()
{
		(*this).num=(*this).num+1;     //无须构造一个临时对象
		return (*this);
}
A A::operator++(int)
{
		A p1;                         //得先构造一个临时对象;
		p1.num=(*this).num;
		(*this).num=(*this).num+1;
		return p1;
}

因为后置++的实现需要构造一个临时对象,当A类型对象(即被操作对象)很大时,其将会花费较长时间去构造对象,所以能用前置++解决的问题不用后置++解决。

**13.掌握变量定义的位置与时机 **
变量的使用规则是 :“当你需要时在定义”,在能满足需要的情况下(因为有时候不得不定义一些作用域较大的变量),尽可能的降低变量作用域的大小。
如果把所有的变量都在程序开头进行定义的话,当别人阅读你的代码时,需要不停地往前去看你这个变量是什么含义(就算是自己很长时间后再来阅读也会这样麻烦),所以在需要使用这个变量时在定义可以较快的使他人阅读自己的代码,增加了代码的可读性。

**14.小心typedef使用中的陷阱 **
不要认为typedef与宏定义等同,二者是有区别的。

#include <iostream>
#define p1 int*
using namespace std;
typedef int* p2
int main()
{
	p1 a,b;
	p2 x,y;
	int c;
	a=&c;
	b=&c;
	x=&c;
	y=&c;
	cout<<a<<endl<<b<<endl<<x<<endl<<y;
	return 0;
}

结果会报错,显示“b=&c”有错,int*类型的值不能赋值给int类型的实体。
也就是b在定义时并没有定义为int *类型的而是定义成立int类型的,这是因为宏定义只是简单地符号替换,而typedef则不同,其可以有int *定义变量时一样的效果。

15. 尽量不要使用可变参数
int nasa(int a,…)其为含有可变参数函数的定义形式。
c语言中的printf(),和scanf()都是含有可变参数的函数,其在使用时可以支持任意数量,任意类型的参数。
虽然这样看似十分方便灵活但是其有很大的缺点。
(1)其十分不安全,其不会进行类型安全检查。

printf("%d,%c".256,256)

因为其printf的第二个参数应该接收字符类型,而其传的是int类型
其会对int类型的数据进行强制类型转换为字符类型,因为字符类型的长度小于int类型的,所以其发生内存截断(如果截断后的数据正好在字符类型数据范围内则不会报错,程序会发生意想不到的错误)。
(2)可变参数不支持自定义数据类型
例如printf函数只能支持基本数据类型,这也是为什么c++用输入输出流对象来进行基本的输入输出的原因之一,cout与cin就支持自定义数据类型(通过运算符重载),而且其使用方便,很随意不需要再像使用printf那样进行格式化输出,而且还极易出错。

c++有自己的方法实现类似于可变参数的这种功能,虽然没有其灵活但是其很安全。(其实就是通过函数重载来实现的)

class A
{
private:
public:
		void nasa(int);
		void nasa(char);
		void nasa(int,char);
		void nasa(char,int);
		......
};

尽量不要使用可变参数,因为其真的很不安全,而且极易出错

16.不要用goto语句(不要用就对了)

17. 小心隐式转换带来的麻烦
显示强制类型转换虽有时候也会带来一定的麻烦但其是人为可控的,容易找到。但是隐式类型转换是编译器自动进行的,是很难察觉到的。
(1)基本类型的隐式转换
基本类型一般在运算时都是向更宽的类型转换。
(2)T到void的隐式转换
c语言支持void与其他类型的指针之间进行双向转换,而c++只支持其他类型的指针转换为void
(即任何类型的指针都可以赋值给void*类型,从而变为不指向任何类型的指针)
(3)子类到父类的隐式转换
即子类对象可以对父类对象进行赋值
(但父类不能隐式转换为子类,即父类对象不能对子类对象进行赋值)
(4)重载类型转换符与转换构造函数中涉及的隐式转换
重载类型转换符引起的隐式转换

class A
{
public:
		int x;
		int y;
public:
		A(int  a,int b):x(a),y(b){}
		operator double()
		{
				return double(x)/double(y)
		}
};
int main()
{
		A P1(1,2);
		cout<<p1;
		return 0;
}

结果输出0.5,没有重载<<运算符为什么可以输出自定义类型呢?
其实因为当编译器没有找到合适的operator<<函数时,其会看能不能把带输出对象转换成能够执行operator<<函数的类型,正好重载的double类型转换符可以把其转换成double类型,所以待操作对象其被自己重载的double函数给隐式转换成double类型
1.0/2.0等于0.5后再调用operator<<函数进行输出
所以当重载了类型转换符的类对象如果没有对输出流对象进行重载的话,不能进行输出(否则会产生意料不到的麻烦)。

转换构造函数引起的隐式转换
(也包括含有一个参数的构造函数)

class A
{
public:
		A(int a):num1(a){}
		A(int a1,char  a2='@'):num1(a2),num2(a1){}
		void output();
private:
		int num1;
		char num2;
};
int main()
{
		int w=2;
		char ww='a';
		A p1(w);
		p1.output();
		p1=ww;
		p1.output();
		return 0;
}
void A::output()
{
		cout<<num1<<" "<<num2<<endl;
} 

结果为:
2
0 a
原因是当p1=ww(一个char类型的对象值理论上是不能赋给一个类类型的对象的,其之所以可以赋值是因为编译器利用此类所含的构造函数A(int a1,char a2=’@’ ):num1(a1),num2(a2)对其进行了隐式转换成此类类型。)
为了解决这个问题应该在含有单参数的构造函数前加上explicit关键字

explicit  A(int a):num1(a){}
explicit  A(int a1,char  a2='@'):num1(a2),num2(a1){}

这时候p1=ww语句就是被认为是不合法的,无法通过编译。

总结(4):由重载类型转换符带来的隐式转换问题,要注意是否重载了operator<<函数,否则不要试图输出本类对象。
由转换构造函数(包含单参数的构造函数)带来的隐式转换问题,注意在其构造函数定义前加上explicit关键字

18. 正确区分void与void *
(1)void是无类型的意思
①函数无返回值时应该将其声明为void类型

void nasa(参数表列)

②函数无参数时应该声明void

返回值类型 nasa(void)

实际可以写可以不写,但是有的编译器在没有void的情况下会报错,为了代码的可移植性,在编程中尽量加上void。
(2)void *(意思是指针不确定其指向什么类型)
不同类型的指针是不能直接相互转换的

int *p1;
char *p2
p1=p2

这是不允许的,其必须通过强制类型转换
p1=(int *)p2 这是合法的
而void *不一样,任何类型的指针都可以转换成void *

void *p1;
int *p2;
p1=p2;

这是允许的,c语言中还允许void *类型直接转换成其他指针类型,但是在c++中就不允许这么干。但是可以强制类型转换
p2=(int *)p1
一般类型的指针支持加上或减去一个数,

int *p;
int a=*(p+2)

p指针加上2表示p指针所指空间再向下偏移两个字节
但是void *类型的不行,因为其无法确定其指向的是什么类型,即无法确定其所指向空间的大小,因此不能进行加减一个数。

posted @ 2019-07-28 17:37  怎么可以吃突突  阅读(100)  评论(0编辑  收藏  举报