• 改善c++程序的150个建议(读后总结)-------10-11


    10. 优化结构体中元素的布局
    结构体变量所占空间大小并不是其所含类型所占字节数之和,其所占内存字节数涉及到字节对齐。
    字节对齐 :变量在内存中储存都是以字节数为单位,每一个字节都有自己的地址,逻辑上变量的首地址(第一个字节的地址)可以是任意位置,但实际不同类型变量其首地址是有一定规则的,这是为了更快的查找便于存取(牺牲空间来换取效率)。
    结构体变量中的各个类型在内存中存放时也按照一定的规则,并不是简简单单的用连续的内存 挨边存放。
    其遵循的规则有:
    (1)结构体变量的首地址可以被其所含最宽的基本类型的大小所整除。
    (2)其结构体变量所含各个基本类型的首地址相对于结构体变量的首地址来说的偏移量应为自身大小的整数倍。(如有需要,编译器会在两个基本类型存储的中间填充字节以满足要求)
    (3)其结构体变量总的宽度应为其所包含的最宽基本类型大小的整数倍。(如有所需,编译器会在其尾部填充字节以满足要求)

    在编程中可以利用上述规则来合理的安排定义数据的顺序减少占用的空间(即减少填充的字节)
    ,也可以自己显式的填充字节(其实显式填充和编译器自动填充一样,这些填充的内存单元都对程序每有意义)------------空间换取时间
    在编程中如果对时间效率要求不高,而空间资源紧张的话可以利用编译器的pack()指令显式的调整结构体的对齐方式。

    #pragma pack{ n }     //n为字节数,其取值为1,2,4,8,16,默认是8
    
    struct  A
    {
    	int a;
    	char b;
    	short c;
    };
    

    如果不用编译器的pack()指令的话则sizeof(A)为8.
    如果利用pack()指令并让n为1的话,其结构体对齐方式为1字节对齐,则sizeof(A)为7
    -----------时间换取空间

    11. 尽量少用强制类型转换
    c++保留了C风格的强制类型转换,而且具有自己新风格的强制转换
    (1)c风格的强制类型转换
    形式为:
    类型标识符(待转换变量)或(类型标识符)待转换变量
    这种强制类型转换比较危险,其会产生一些难以发现的问题,例如

    int a=65536;
    int i=unsigned short(a);
    

    i的值为0;
    因为在32位的机器中int型的数据占4个字节范围为-2147483648~2147483647
    而unsigned short类型的数据占2个字节范围为
    0~65535
    当把一个大于65535的int类型数据强制转换为unsigned short类型数据时发生内存截断(4个字节截取后两个字节)。相同如果把unsigned类型的数据转换为int类型则会发生内存扩张
    (2)c++新式风格的强制类型转换
    ① const_cast<T*>(a)
    其可以去除类中的const ,volatile,和__unaligned属性。

    calss a
    {
    	//code
    };
    int main()
    {
    	const  a *p1 =new a;       //p1为一个指向常对象的指针变量
    	a *p2=p1;                 //错误,const指针不能赋值给非const指针
    	a *p3=const_cast<a*>(p1)   //正确,
    	                      
    

    利用const_cast<T*>(a)对p1常指针进行强制类型转换去除其const属性,使其可以赋值给非const指针

    ②dynamic_cast<T*>(a)
    其与c++的动态多态性有关。
    c++中通过把基类函数设置为虚函数来实现动态多态性,即可以通过定义基类指针指向不同继承类对象来调用其相关方法,前提是这种继承类对象的相关方法得在基类中声明为虚函数,否则用基类指针无法直接调用。 这时候就需要获得这种继承类对象的类型,通过dynamic_cast<T*>(a)来强制转换其指针类型从而调用其特有方法

    calss A
    {
    	//code
    };
    class B:public A
    {
    	//code
    };
    class C:public B
    {
    	//code
    };
    int main()
    {
    	A *p1=new C;
    	C *p2=dynamic_cast<C*>(p1);     //正确,安全p2为C类型的指针
    	B *p3=dynamic_cast<B*>(p1);     //错误,不安全p3为空指针
    	return 0;
    }	
    

    通过p2指针就可以调用c类型特有的方法。
    dynamic_cast<T*>(a)其是通过对类名称通过字符串比较来实现的,其在继承中所处的层次越深,其字符串比较的次数就越多(为了找到对应类名称),需要花费很大时间,不如不用直接定义对应类型的指针;
    ③reinterpret_cast<T*>(a)
    它用于不同类类型指针之间的转换;

    class A
    {
    	.....
    }
    class B
    {
    	......
    }
    int main()
    {
    	A *p1=new A;
    	B *p2=reinterpret_cast<B*>(A);
    	return 0;
    }
    

    因为A与B是不同的两个类型,其具有不同的内存储存形式,强行对其转换可能会发生内存扩张或内存截断。

    ④static_cast<*T>(a)

    **总结:**由上述可得在程序中使用强制类型转换会带来一系列安全问题,且不易发觉因此在程序中尽量少使用强制类型转换。

  • 相关阅读:
    JavaScript
    并发编程基础
    基于 TCP & UDP 协议的 socket 通信
    struct 模块 & subprocess 模块
    Python中的异常处理
    网络编程基础
    Json 模块补充
    冒泡排序
    OOP 反射 & 元类
    OOP 内置函数
  • 原文地址:https://www.cnblogs.com/revercc/p/13287094.html
Copyright © 2020-2023  润新知