如何提高c语言编程能力,提高c++性能的编程技术
Yyds干货库存
我的主页:畅游码海
欢迎大家喜欢,收藏,关注我!我会继续更新更多高质量的原创文章!
一、习惯C 01条款:把C当成一门语言。联邦C不是一个有一套规则的集成语言:它是由四个子语言(C,面向对象C,Template,STL)组成的联邦政府,每个子语言都有自己的规约。记住这四个是仅次于语言的,你会发现C要好理解得多。
条款02:尝试用const、enum、inline替换# define # define aspect _ ratio 1.653。
例如,上面的句子是由预处理器而不是编译器处理的。有可能ASPECT_RATIO没有进入token表,所以如果出现编译错误,编译器会提示错误信息是1.653而不是ASPECT_RATIO,你会觉得很困惑。
解决方法是用常量替换宏。
常数双精度比率=1.653
这样编译器就能看到ASPECT_RATIO,使用常量会减少代码量,因为预处理程序只会盲目替换,会出现1.653的多个副本。
String对象通常比char*好一点
对于类的独占常量,为了限制类内的作用域,防止多个实体,最好使用static。
如果您的编译器在类中声明const静态整数类型时支持初始值,则使用它。如果没有,在类中定义它,并在相应的实现文件中赋值。如果需要在编译器中使用类常数值,最好用enum代替,而且enum不能用来取地址,所以不会分配额外的存储空间。
对于看起来像函数的宏,最好使用内联模板函数。
条款03:尽量用const const。当目标出现在星号的左侧时,意味着该对象是常数。当它出现在星号的右边时,意味着指针本身是常数。如果它出现在两边,指针和对象都是常量。
Void f1(const Widget* pw)和void f2(Widget const* pw)含义相同,都表示所指对象是常数。
对于STL迭代器,如果希望迭代器指向的动态部分不变,就需要const_iterator。
让函数返回一个常量值,往往可以减少客户错误(比如给返回值赋值)导致的事故。
用成员函数实现const的目的是为了明确成员函数可以作用于const对象:
我们使类接口更容易理解。我们把可以操作const对象的const成员函数和非常数成员函数重载,也就是可以同时出现。当传入不同的参数时,会调用不同的版本,但有时我们需要这样,但又不想重复代码。我们可以在非const成员中调用const成员函数来处理这种代码重复问题。
示例:const _ cast char(static _ cast const文本块(* this)[position]);在这之后,内部的第一个安全转换使const版本被调用,然后外部进行const转换。
确保对象在使用前已经初始化。对于内置类型,应该手动初始化。
构造函数最好使用成员的初始值列表,不要在构造函数中使用赋值操作初始化。而且在初始值列表中列出的成员变量的顺序应该和类中声明的顺序一致,因为声明顺序是c保证的初始化顺序。
对于静态对象,编译单元之间的初始化顺序是无法确定的,因为C只保证在这个文件中使用之前必须初始化。
例如,这个问题可以通过用loacl static object:class file system {.};
文件系统tfs(){
静态文件系统fs;
返回fs;
}二。构造/析构/赋值操作子句05:知道C默默编写和调用哪些函数。如果你没有定义它们,编译器会自动帮你练习默认的*构造函数、析构函数、复制赋值操作符和复制构造函数*,但以下情况不会为你生成默认的复制赋值操作符。
基类中带有被引用成员变量和带有const的成员变量的复制赋值操作符是私有成员函数子句06:如果不想使用编译器自动生成的函数,应该明确拒绝。当我们不想让编译器帮我们生成对应的成员函数时,可以声明为私有,不实现。
条款07:为多态基类声明虚析构函数:在下列情况下,应该为类声明虚析构函数:
作为一个多态基类,每个类中都有任何虚函数。如果该类不是设计用来作为基类的,那么就不应该用虚析构函数来声明它。
不要让异常从析构函数中逃脱。析构函数不会抛出异常。如果真的要抛出异常,最好使用STD:abort();并把它放在捕捉器中来抑制这种行为。
如果一个动作可能抛出异常,那么最好把它放在一个普通的函数中,而不是放在析构函数中,让客户执行函数并处理它。
条款09:在构造和销毁中永远不要调用虚函数。在构造和销毁过程中,不要试图调用虚函数或者在被调用的函数中调用虚函数,因为会调用父类版本,会造成一些未定义的错误。
其中一个解决方案是:
类别交易{
public ci:
显式事务(const STD:string log info);
void log transaction(const STD:string logIngo)const;//把它变成这样一个非虚函数
.
};
Transaction:Transaction(const STD:string log info){
.
log transaction(log info);//这样打电话
}
类BuyTransaction:公共事务{
buy transaction(parameters):transaction(createlogstring(parameters)){.}//将日志信息传递给基类的构造函数。
私人:
static STD:string createLogString(参数);//注意,这个函数是一个静态函数
}条款10: Let operator=返回对*this的引用。为了实现内置类型x=y=z=15这样的链式赋值,因为=采用右结合律,所以等价于x=(y=(z=15))。因此,为了使我们的自定义类也实现,我们* *重载=,=,-=,*=来使它
第11条:在operator=中处理“自赋值”时,赋值时会发生自赋值。在这种情况下,我们很容易写出不安全的代码。
Widget:operator=(常量Widget rhs){
删除Pb;//给自己自由
pb=新位图(* RHS . Pb);//这不安全。
返回* this
}因此有三种推荐做法
# # # # #先验证是否相同,是否为自赋widget:operator=(const widget RHS){
if(this==rhs)返回* this//验证它是否相同
删除Pb;
pb=新位图(* RHS . Pb);
返回* this
} # # # # #在复制它引用的内容之前不要删除Pb widget:operator=(const widget RHS ){
Bitmap * pOrig=pb
pb=新位图(* RHS . Pb);//让pb指向*pb的副本
删除pOrig//删除原pb
返回* this
} # # # # #使用类小部件{
.
无效交换(部件RHS);//交换*this和rhs的数据
.
};
Widget:operator=(常量Widget rhs){
部件温度(RHS);//创建rhs的副本
交换(临时);//交换*这个和上面的副本
返回* this
}条款12:复制一个对象的时候,不要忘记它的每一个组件。为了保证复制时对象中的所有成员变量都被复制,我们要在word类的构造和赋值函数中调用父类的构造和赋值函数来完成各自的任务。
不要试图在复制构造函数和赋值函数中互相调用。如果你想消除重复的代码,创建一个新的成员函数,最好是私有的并命名为init。
三。资源管理条款13:防止资源泄露,在构造函数中获取资源,在析构函数中释放资源,可以有效避免资源泄露。
使用智能指针是个好主意。auto_ptr在C 11中已经被抛弃,常用的有三种:unique_ptr、share_ptr和weak_ptr。
第十四条:资源管理类中心的复制行为我们在管理RAII(在构造函数中获取,在析构函数中释放)概念的类时,要根据不同的用途,处理不同的情况。
当我们处理不能同步拥有的资源时,只能使用禁止复制。例如,当复制操作被声明为私有的,并且我们想要一起拥有资源时,我们可以使用引用计数方法。例如,当我们需要使用shared_ptr进行复制时,可以使用底层资源所有权转移的方法。条款15:资源管理类中一些提供访问原始资源的api函数,经常需要访问该类的原始资源,所以每个RAII类都应该提供一个原始资源,返回给它管理。
您可以使用显式转换或隐式转换来返回原始资源,但使用显式转换通常更安全,而使用隐式转换更方便。
类别字体{
.
font handle get()const { return f;}//显示转换
.
运算符font handle()const { return f;}//隐式转换函数
.
私人:
font handle f;//管理的原始资源
}条款16:成对使用new和delete时,使用相同的形式。不要typedef数组形式,因为这将导致调用delete ptr而不是delete [] ptr,后者将是未定义的或者对内置类型有害。对于类的类型,不能调用剩余的析构函数,导致类中托管的资源没有被释放,从而造成内存泄漏。
如果[]用在新表达式中,则[]也用在相应的删除表达式中。
第十七条:把newed对象放在有独立语句的智能指针里,比如语句处理小部件(STD:TR1:shared _ ptr Widget(new Widget),priority())。
优先级函数的执行顺序可以在执行newwidget 语句和调用std:tr1:shared_ptr构造函数之间确定,可以在它们的前面,也可以在它们的中间。因此,newd对象应该作为一个独立的语句存储在智能指针中,并被分开。否则,一旦抛出异常,可能会导致潜在的资源泄漏。
四。设计与声明条款18:使界面易于被正确使用,不易被误用。我们的界面应该为客户着想,以防止他们犯错误。比如在给函数传递日期的时候,把日期参数做成类的形式,用静态成员函数返回固定的月份,避免用户参数写错。
界面要和内置的界面保持一致,避免让客户感觉不舒服。STL在这方面做得很好。
Tr1:shared_ptr支持自定义删除,可以用来防止跨DLL构建和删除,可以用来自动解锁互斥。
设计一个类就像设计一个类型。精心设计一个类应该遵循以下规范
构造类的构造函数、析构函数、内存分配函数和释放函数会混淆初始化和赋值。如果您的类需要通过值传递,复制构造函数应该设计一个通过值传递的版本。应该对成员变量施加约束,以确保它们是合法的值,因此成员函数必须进行错误检查。如果是派生类,就要遵守基类的一些规范,比如析构函数是否为virtural,你的类是否允许有转换函数,是否允许。如果只允许存在显式构造函数,那么就必须编写专门负责转换的函数。知道你的类的哪些函数和成员应该被设计成私有的。应该是你的朋友,把他们和另一个人套在一起是否合理。在效率、异常安全和资源利用方面提供了哪些保证?如果您定义了整个类型族而不是一个新类型,那么您应该定义一个类模板。如果你只是定义新的word类来给现有的类添加机制,也许简单地定义一个或多个非成员函数或模板更好。第20条:最好用通过引用传递到常量来代替通过值传递。尝试用通过引用传递到常量来代替通过值传递,因为前者通常更有效。比如在传递类的时候,避免了多次调用构造函数和析构函数,大大提高了效率。
但是对于一些,比如*内置类型,迭代器,函数调用*等。最好是传递值。
第21条:当你必须返回一个对象时,不要考虑返回它的引用。永远不要返回指向临时变量的指针或引用,因为它存在于堆栈中。一旦函数调用结束,你会得到一个坏指针,这是静态变量无法解决的。你可以通过返回值来解决它。
第22条:将成员变量声明为private确保了一致性,精细划分了访问和控制,约束部门的变化不影响protected的使用,protected的封装性不比public强。
第23条:最好用非成员、非友元替换成员函数,我们可以用非成员、非友元函数替换部分成员函数,这样可以增加类的封装性、包装灵活性、可扩展性。
第24条:如果所有参数都需要类型转换,请使用非成员函数。如果你需要对一个函数的所有参数进行类型转换(包括这个指针所指向的隐喻参数),那么这个函数必须是非成员。
第25条:考虑写一个不抛出异常的交换函数。如果你没有定义一个交换函数,编译器会为你调用一个通用的交换函数,但是有时候这样效率不高,因为默认情况下它在替换指针的时候会替换整个内存。
我们采取解决方案。
类中提供了一个公共交换成员函数,该函数不能引发异常。类的命名空间中提供了一个非成员交换函数,它调用类中的交换函数。如果你写的是类而不是模板类,那么为你的类专门化std:swap函数,让它调用你的swap函数在类中用std:swap声明,并让它暴露出来,这样编译器就可以自己选择更合适的版本。5.实现条款26:如果你尽可能晚地定义一个变量,你将不得不承担构造和析构该变量的成本和时间。所以在定义一个变量的时候,要尽可能的推迟定义时间,在使用之前定义好,避免定义了却不用的时候浪费。
第27条:尽可能少做改造。旧的转换是C风格的转换,C提供了四种新的转换:
##### const_cast通常用来划分物体的恒常性。也是唯一具有此能力的变换运算符# # ##### dynamic_cast,主要用于执行“安全向下变换”,即决定一个对象是否属于继承系统中的某个类型。它是唯一一个旧语法无法执行的动作,而唯一一个可能会耗费大量运行成本的转换动作##### reinterpret_cast则是打算执行低级转换。实际的操作(和结果)可能取决于编译器,这意味着它不是可移植的。例如,将指向int的指针转换为int。这种转换在低级代码之外很少见。##### static_cast用于强制隐式转换,比如将非const对象转换为const对象,或者将int转换为double等。它还可以用于执行上述转换的反向转换,例如将void*指针更改为类型指针,将指向基的指针更改为指针ro派生的指针。但是它不能将const转换为non-const ——。这是只有const_cast可以做旧式转换的时候了。当调用显式构造函数将对象传递给函数时,其他人会尝试使用新型转换。
请记住以下几点:
如果可以,避免dynamic_cast转换。如果真的需要,可以尝试用其他非改造方案代替改造。如果有必要,那么你应该把它隐藏在一个函数后面,客户可以在以后调用这个函数,而不是把转换放到自己的代码中。你需要新的转换,不要用旧的转换条款28:避免向对象内部组件返回句柄,避免向内部对象返回句柄(包括引用、指针和迭代器)。这可以增加封装并减少空指针的可能性。
第29条:争取“异常安全”是值得的。异常安全功能提供以下三种保证之一:
基本承诺:如果抛出异常,程序中的任何东西都将保持有效状态。这不会损坏任何对象或数据,所有对象都处于内部一致的状态。然而,程序的实际状态是不可预测的。
强保证:如果抛出异常,程序状态不会改变。调用这样的函数需要认识到,如果函数成功了,就是完全成功了,如果函数失败了,程序就会恢复到调用前的状态。
不要抛出保证:承诺从来不会抛出异常,因为它们总能实现最初承诺的功能。所有作用于内置类型的操作都提供了非行保证,这是异常安全代码中必不可少的关键基础材料。
这三个保障是在增加的,但是如果真的做不到,可以提供第一个基本的承诺,写作的时候要思考如何让它格外安全。
首先用对象管理资源可以防止资源泄漏。如果可以实现,尽量满足上面的最高级条款30:彻底理解inlining的in-out inline语句的两种方法:
Yu inline application,即定义写在类中,这意味着在定义前添加关键字inline将大部分内联限制在小而频繁调用的函数。这可以使将来的调试和二进制升级变得更容易,也可以最小化潜在的代码膨胀问题。
不要仅仅因为函数模板出现在头文件中就将其声明为内联的。
第31条:最小化文件间的编译依赖。“最小化编译依赖”的想法是依靠声明性表达式,而不是定义性表达式。
文件从实现中分离出来,头文件完全且仅声明性地用于创建接口类VI。继承和面向对象设计条款32:确保你的公共继承塑造了is-a关系。公共继承意味着is-a关系,即子类是父类的专门化,它必须适合适合基类的子类。每个派生类对象包含父类对象的特征。
第33条:避免掩盖继承的名字。父类中的名字会被单词类的名字覆盖,尤其是在公共继承下。没有人希望这种事情发生。
为了避免被遮蔽,你可以使用using声明或者传递函数把它赋予子类。
条款34:区分接口继承和接口实现声明的目的是让派生类只继承函数接口。
声明虚函数的目的是让派生类继承函数的接口和默认实现。
声明普通函数的目的是强制派生类接受自己的代码,而无需重新定义。
条款35:考虑虚函数的替代品条款36:永远不要重定义继承的非虚函数。在任何情况下都不应该重新定义继承的非虚函数。
第37条:永远不要重新定义继承的默认参数值。永远不要重定义继承的默认参数值,因为默认参数值是静态绑定的,在虚函数3354中你唯一应该覆盖的是动态绑定。
第38条:通过复合成型has-a或“根据某物实现”来区分公共继承和复合
在应用领域,复合是指一个包含另一个,即有-a关系;
在实现领域,指的是根据某件事去实现。
第39条:明智而谨慎地使用私人遗产。需要复合时,尽可能复合,仅在必要时使用私有:
当涉及受保护成员或虚拟函数时
当空间受到威胁时,尺寸需要最小化。
第40条:在明智谨慎地使用多重继承和多重继承时,如果其父类继承了同一个父类,解决方法是使用虚拟继承,即其父类同时在虚拟中继承那个父类,但会有一些相应的代价,比如时间较慢,需要重新定义父类的初始化。所以设计的时候最好不要让这个父类有任何数据成员。
当单一继承和多重继承都可以的时候,那么最好选择单一继承,它也有正当的用途,可以同时实现公有继承和私有继承的结合。
七。模板和泛型编程术语41:理解隐式接口和编译时多态显式接口:它由函数的签名(即函数名、参数类型和返回类型)组成。
隐式接口:它不基于函数签名,而是由有效的表达式组成。
面向对象和泛型编程都支持接口和多态,但一个是显式的,一个是隐式的。
有两种多态,一种在运行时,一种在编译时。
第42条:在理解typename的双义声明模板的参数时,class和typename是可以互换的,没有区别。
但是,在标识嵌套的从属类型名时,必须使用typename。
不能在基类列(继承时)或成员的初始值列(初始化列表)中用作基类修饰符。
模板类型名T
class derived:public base t:nested {//不能将“typename”添加到基类列表中。
公共:
explicit derived(int x):base t:nested(x){//mem . init . list不允许“typename”
typename Base T:Nested temp;//这是嵌套的依赖类型名称。
.//作为基类修饰符,需要添加typename。
}
}条款43:学习在模板化基类中处理名字。模板化基类是指当派生类的基类是模板时。
在派生类中使用父类的函数时,编译器会报错,因为模板实例化时编译器无法确定是否真的有那个函数,所以有三种方法。
在调用基类函数之前添加this- using声明,并告诉编译器,让它假设基类中存在这个函数,以基类:function的形式编写(不建议这样,因为如果是虚函数,会影响动态绑定)。但是,当模板完全专门化时,如果它不使用这个函数,它仍然会报告一个错误。
第44条:模板通过提取与参数无关的代码生成多个类和函数,所以任何模板代码都不应该依赖于一个导致膨胀的模板参数。
由非类型模板参数引起的代码膨胀通常可以通过用函数参数或类成员变量替换模板参数来消除。
由类型模板参数引起的代码膨胀通常可以通过与具有相同二进制表达式的特定类型共享实现代码来减少。
第45条:使用成员函数模板接受所有兼容的类型。使用成员函数模板生成接受所有兼容类型的函数。
如果您声明成员函数模板用于一般化复制构造函数和赋值操作,那么您还需要声明普通的复制构造函数和赋值操作。
第46条:当需要类型转换时,请为模板定义非成员函数。当我们写一个模板类的时候,它提供的函数和这个模板的吉光支持所有参数的隐式类型转换。模板类中哪些函数应该定义为友元函数?
八。自定义new和delete子句49:了解new-handler的行为当new分配失败时,它会首先调用客户指定的一个错误处理函数(set_new_handler),即所谓的new-handler。
它是一个typedef,定义了一个没有参数且不返回任何内容的指针对指针函数。
set_new_handler的参数是一个指针,指向当运算符new无法分配足够的内存时应该调用的函数。返回值也是一个指针,指向在调用set_new_handler之前正在执行的new-handler函数(即将被替换)。
一个设计良好的new-handler函数必须做到以下几点:
可以使用更多的内存。这种策略的一种方式是,程序在开始时分配一大块内存,然后当它第一次被调用时,它被释放回程序以安装另一个新的处理程序。你可以设置它调用另一个新的处理程序来代替你做不同的事情。方法是调用set_new_handler加载new-handler,也就是向set_new_handler传递一个空指针,这样new在分配不成功时抛出bad_alloc异常。Return,调用abort或exitC并支持类的独占new-handler,但这不是必需的。您可以让每个类提供自己的set_new_handler和operator new。
Set_new_handler允许客户指定一个在内存分配无法满足时调用的函数。
Nothrow new是一个相当有限的工具,因为它只适用于内存分配:后续的构造函数调用仍然可能抛出异常。
第50条:了解新、删的合理更换时间。替换operator new或operator delete的三个常见原因:
用于检测应用程序中的错误。
为了收集使用情况的统计数据
为了提高分发和返回的速度
为了减少默认内存管理器带来的额外空间开销,也就是实现内存池,可以节省空间。
为了补偿默认分配器中的非最佳对齐
为了聚集相关的对象
为了获得非传统的行为
知道什么时候合理地替换默认的新的和删除的基础上“全局”或“类特定的”
第51条:在编写new和delete时,我们应该坚持传统的操作符new应该包含一个无限循环,并尝试在其中分配内存。如果它不能满足内存需求,我们应该调用new-handler。还应该能处理0字节的应用,也就是按照1字节分配。Class的独占版本要处理“比正确大小更大(错误)大小的应用”,因为当有word类继承时,传入的大小和父类大小会有差异,所以需要做出类似if(size!=sizeof(父类))
当操作符delete接收到一个空指针时,它应该什么也不做,如果需要的话,把它留给全局操作符new来处理。
第52条:当你写新位置时,你也应该写删除位置。当您编写一个新的放置运算符时,请确保您也编写了相应的放置运算符删除版本。如果不这样做,可能会出现细微的、间歇性的内存泄漏。
当您声明placement new和placement delete时,请确保不要无意识地(无意地)覆盖正常的全局版本。如果你想提供自定义的表单,请包括所有正常表单的新建和删除或使用继承机制和使用声明。
九。杂项讨论条款53:不要忽视编译器的警告。不同的编译器有不同的警告标准,编译器发出的警告信息要引起重视。争取你编译器最高警告级别的“无警告”荣誉。
不要过分依赖编译者的报警能力,因为不同的编译者对事物的态度是不同的。一旦移植到另一个编译器上,你原本依赖的警告信息可能会消失。
第54条:熟悉包括TR1在内的标准库。C标准库的主要函数由STL、iostream和locales组成。并包含C99标准库。
TR1增加了对智能指针(如tr1:shared_ptr)、广义函数指针(tr1:function)、基于哈希的容器、正则表达式等10个组件的支持。
TR1。自知之明。为了获得TR1提供的好处,您需要一个物理副本。增加了良好材料来源。
第55条:让自己熟悉BoostBoost这个社区和网站。致力于开发免费、开源和同行评审的C库。对Boost C的标准化起到了有影响的作用。
Boost TR1组件和许多其他库有许多实现。
欢迎大家喜欢,收藏,关注我!
,
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。