EC1-3

1.视C++为一个语言联邦

c++并不是一个语言,它是由4个次语言组成,分别是C、Object-Oriented C++、Template C++和STL。(其实这4部分不仅可以体现c++的主要特性,也可以从4个方面学习c++,并可以从这4个方面验证自己对c++了解和掌握程度)。因此它的高效编程守则对于同一个对象也会有多种要求。所以c++高效编程守则要视状况而变化,因为这取决于你当前状况偏向于哪个次语言。
举个栗子:传参时候并不都是选择pass-by-reference就是高效,要看其偏向于哪个次语言:如果是内置类型,比如int这种自带的,因为其偏向于C语言,所以pass-by-value会更有效;如果是对象,其偏向于Object-Oriented C++,所以pass-by-reference更高效,所以要具体情况具体分析~

总结:选择哪种高效的原则要看当时状况偏向哪个次语言。

2. 尽量以const、enum、inline替换#define

1)由于#define定义的常量名未进入symbol table,eg:#define ARRAY_NUM 10 调试时只能看到数字10而不能看到名字ARRAY_NUM,所以导致调试时看到该数字会有点莫名其妙。

2)不能对#define或enum定义的变量名取地址,这个是跟他们存放的内存相关吧~

3)宏定义后,若在某处被#undef,其后该宏就会无效

4)编译器要在编译期间知道数组的大小,所以不能酱紫:

int nArrayLength = 10;
int array[nArrayLength];

但是可以酱紫:

const int nArrayLength = 10;
// 或者
enum{nArrayLength = 10};
int array[nArrayLength];

是因为const修饰后的变量存放位置就不同了?

c++中,一个const不是必需创建内存空间,而在c中,一个const总是需要一块内存空间。

在c++中是否要为const全局变量分配内存空间,取决于这个const变量的用途,如果是充当着一个值替换(即就是将一个变量名替换为一个值),那么就不分配内存空间,不过当对这个const全局变量取地址或者使用extern时,会分配内存,存储在只读数据段。也是不能修改的。

c++中对于局部的const变量要区别对待:

对于基础数据类型,也就是const int a = 10这种,编译器会把它放到符号表中,不分配内存,当对其取地址时,会分配内存
对于基础数据类型,如果用一个变量初始化const变量,如果const int a = b,那么也是会给a分配内存
对于自定数据类型,比如类对象,那么也会分配内存。

c中const默认为外部连接,c++中const默认为内部连接.当c语言两个文件中都有const int a的时候,编译器会报重定义的错误。而在c++中,则不会,因为c++中的const默认是内部连接的。如果想让c++中的const具有外部连接,必须显示声明为: extern const int a = 10。
————————————————
原文链接:https://blog.csdn.net/woainilixuhao/article/details/86521357

根据资料可知,因为const修饰后的变量nArrayLength存放在符号表,所以编译器在编译期间就知道了数组大小,所以编译通过。

5)相比于函数调用,为啥说宏不会带来额外开销?

因为额外开销指的是开辟栈空间存放参数和返回值。

首先,函数调用会带来额外的开销,它需要开辟一片栈空间,记录返回地址,将形参压栈,从函数返回还要释放堆栈。这种开销不仅会降低代码效率,而且代码量也会大大增加,而使用宏定义则在代码规模和速度方面都比函数更胜一筹;其次,函数的参数必须被声明为一种特定的类型,所以它只能在类型合适的表达式上使用,我们如果要比较两个浮点型的大小,就不得不再写一个专门针对浮点型的比较函数。反之,上面的那个宏定义可以用于整形、长整形、单浮点型、双浮点型以及其他任何可以用“>”操作符比较值大小的类型,也就是说,宏是与类型无关的。

6)虽然宏不会带来额外的开销,但是有时候容易使用不当,导致不能得到预期结果;并且宏不会检查参数类型。
eg:

#define CALL_MAX_VALUE(a, b) f((a) > (b) ? (a) : (b))
int a = 5, b = 0;
CALL_MAX_VALUE(++a, b);
// 此时a=7,因为传入参数时+1,返回时又+1了

但可以使用template inline函数替代宏:

template<typename T>
    inline T CallWithMax(const T& a, const T& b)
{
     return (a > b ? a : b);
}
从逻辑上来说,编译器对inline函数的处理步骤一般如下:
(1)将inline函数体复制到inline函数调用处;
(2)为所用inline函数中的局部变量分配内存空间;
(3)将inline函数的的输入参数和返回值映射到调用方法的局部变量空间中;
(4)如果inline函数有多个返回点,将其转变为inline函数代码块末尾的分支(使用GOTO)。

inline函数相对宏函数有如下优点:
(1)内联函数同宏函数一样将在被调用处进行代码展开,省去了参数压栈、栈帧开辟与回收,结果返回等,从而提高程序运行速度。
(2)内联函数相比宏函数来说,在代码展开时,会做安全检查或自动类型转换(同普通函数),而宏定义则不会。
————————————————
原文链接:https://blog.csdn.net/K346K346/article/details/52065524

可以将内联理解为C++中对于函数专有的宏,对于C的函数宏的一种改进。对于常量宏,C++提供const替代;而对于函数宏,C++提供的方案则是inline。并且,inline对声明没有作用,必须要在函数的定义时使用inline

总结

关于#define 变量名,由于**#define 变量名 值的变量名是不会出现在符号表,不便于调试,所以用const 类型 变量名 值**替代。(虽然这么说,但是目前在项目中基本上都是用#define变量名。。。。)

关于#define 函数,由于#define 函数不会检查传入参数的类型以及容易使用不当导致得到结果不合预期,所以用inline替代(在函数定义时使用inline修饰),只有短小代码段才用inline,不然也很浪费内存~

3. 尽可能用const

1)对指针使用const:

const int * p; // 由于const是修饰*p,所以这个const约束的是p指向的内容,即指针指向的内容

int * const p; // 由于const是修饰p,所以这个const约束的p,即指针的值

2)对迭代器使用const:(迭代器类似指针,但是又不同于指针,所以const对其修饰不能用上面的来理解)

const vector<int>::iterator iter;  // const修饰的是迭代器,所以迭代器不能变

vector<int>::const_iterator iter; // const修饰的是迭代器指向的内容,所以迭代器指向的内容不能变

3)const修饰返回值,使函数返回一个常量值,可以避免出现“想要键入==却键成=的错误”。

例子:

Rantional a,b,c;

if(a*b = c)
    ...

因为a、b不是内置类型,所以编译器不会报错,导致if并没有判断a*b的值是否等于c就直接进入这个条件语句了,导致逻辑出错。

4)const修饰成员函数:不可以改动对象内容,用于操作处理const对象。
另外,编译器对const成员函数是执行bitwise constness,即成员函数不能改变对象的任何non-static的成员变量,因为static变量并不属于该类了,他是属于大家的。
通过用mutable修饰变量可以释放const成员函数的约束。

5)两个成员函数如果只是常量性不同,那么可以被重载,eg:

class XX
{
int func() const; // 创建的XX对象是const的,那么就会调用这个func
int func(); // 创建的XX对象是非const的,那么就会调用这个func
}
方法重载是指同一个类中的多个方法具有相同的名字,但这些方法具有不同的参数列表,即参数的数量或参数类型不能完全相同.
重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准。

6)用const_cast去掉const,用static_cast加上const,从而实现令non-const版本调用const版本可避免代码重复性。

eg:

const char& operator[](std::size_t position) const
{
    // 边界检验等其他操作
    ...
    return text[position];
}
char& operator[](std::size_t position)
{
    return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);
}

Q: 书本提到将“边界检验等其他操作”这部分代码移到另一个成员函数中,然后两个版本的operator[]调用它会重复一些代码,例如函数调用,两次return语句等。那为啥这种常量性移除调用[]就避免了代码重复?

总结

关于const指针, 记住const指针和const迭代器的理解是相反的;
关于const成员函数,当成员函数参数和返回值都一样,就一个有const,一个没有const,这也是一种重载;mutable修饰成员变量就可以去掉const成员函数的对该成员变量的约束,PS:const成员函数仅仅约束的是对成员变量的更改。
关于加减const:const_cast是去掉const,static_cast是加上cons。

插播:static_cast常被用作显式类型转换使用;const_cast修改类型的const或volatile属性。(volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。即volatile的变量是说这变量可能会被意想不到地改变,所以每次都要重新读值)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值