当前位置: 首页 > news >正文

C++语言编程规范-初始化和类型转换

01  

C++语言编程规范-常量

02  

初始化和类型转换

声明、定义与初始化

03  

禁止用 memcpy、memset 初始化非 POD 对象

说明:POD 全称是“Plain Old Data”,是 C++ 98 标准(ISO/IEC 14882, first edition, 1998-09-01)中引入的一个概念, POD 类型主要包括 int, char, float,double,enumeration,void指针等原始类型及其集合类型,不能使用封装和面对对象特性(如用户定义的构造/赋值/析构函数、基类、虚函 数等)。

由于非 POD 类型比如非集合类型的 class 对象,可能存在虚函数,内存布局不确定,跟编译器有关,滥用内存拷贝可能会导致严重的问题。 即使对集合类型的 class,使用直接的内存拷贝和比较,破坏了信息隐蔽和数据保护的作用,也不提倡 memcpy、memset 操作。

示例:×××产品程序异常退出(core dump)。 经过现场环境的模似,程序产生 COREDUMP,其原因是:

在初始化函数内使用

     memset(this,0,sizeof(*this))

    进行了类的初始化,将类的虚函数表指针被清空,从而导致使用空指针。

    解决方案:使用 C++构造函数初始化,不要使用 memset 函数初始化类对象。

    04  

    变量使用时才声明并初始化

    说明:变量在使用前未赋初值,是常见的低级编程错误。使用前才声明变量并同时初始化,非常方便 地避免了此类低级错误。在函数开始位置声明所有变量,后面才使用变量,作用域覆盖整个函数实现,容易导致如下问题:

    ⚫ 程序难以理解和维护:变量的定义与使用分离。

    ⚫ 变量难以合理初始化:在函数开始时,经常没有足够的信息进行变量初始化,往往用某个默认的空值(比如零)来初始化,这通常是一种浪费,如果变量在被赋于有效值以前使用,还会导致错误。遵循变量作用域最小化原则与就近声明原则,使得代码更容易阅读,方便了解变量的类型和初始值。特别是,应使用初始化的方式替代声明再赋值。

    示例:

      //不好的例子:声明与初始化分离string name; //声明时未初始化:调用缺省构造函数//……. name=”zhangsan”; //再次调用赋值操作符函数;声明与定义在不同的地方,理解相对困难//好的例子:声明与初始化一体,理解相对容易string name(”zhangsan”); //调用一次构造函数

      05  

      避免构造函数做复杂的初始化,可以使用“init”函数

      说明:正如函数的变量都在函数内部初始化一样,类数据成员最好的初始化场所就是构造函数,数据成员都应该尽量在构造函数中初始化。

      以下情况可以使用 init()函数来初始化:

      ⚫ 需要提供初始化返回信息。

      ⚫ 数据成员初始化可能抛异常。

      ⚫ 数据成员初始化失败会造成该类对象初始化失败,引起不确定状态。

      ⚫ 数据成员初始化依赖 this 指针:构造函数没结束,对象就没有构造出来,构造函数内不能使用 this 成员;

      ⚫ 数据成员初始化需要调用虚函数。在构造函数和析构函数中调用虚函数,会导致未定义的行为。

      示例:数据成员初始化可能抛异常:

        class CPPRule {public:   CPPRule():size_(0), res (null) {}; //仅进行值初始化  long init(int size)   {     //根据传入的参数初始化size_, 分配资源res   } private:   int size_;   ResourcePtr* res; }; //使用方法:CPPRule a; a.init(100);

        06  

        初始化列表要严格按照成员声明顺序来初始化它们

        说明:编译器会按照数据成员在类定义中声明的顺序进行初始化,而不是按照初始化列表中的顺序,

        如果打乱初始化列表的顺序实际上不起作用,但会造成阅读和理解上的混淆;特别是成员变量之间存在依赖关系时可能导致 BUG。

        示例:

          //不好的例子:初始化顺序与声明顺序不一致class Employee public: Employee(const char* firstName, const char* lastName) : firstName_(firstName), lastName_(lastName) , email_(firstName_ + "." + lastName_ + "@huawei.com") {}; private: string email_, firstName_, lastName_; };

          类定义 email_是在 firstName_,lastName_之前声明,它将首先初始化,但使用了未初始化的 firstName_和lastName_,导致错误。在成员声明时,应按照成员相互依赖关系按顺序声明。

          07  

          类型转换

          避免使用类型分支来定制行为:类型分支来定制行为容易出错,是企图用 C++编写 C 代码的明显标志。

          这是一种很不灵活的技术,要添加新类型时,如果忘记修改所有分支,编译器也不会告知。使用模板和虚函数,让类型自己而不是调用它们的代码来决定行为。

          08  

          使用 C++风格的类型转换,不要使用 C 风格的类型转换

          说明:C++的类型转换由于采用关键字,更醒目,更容易查找,编程中强迫程序员多停留思考片刻,谨慎使用强制转换。

          C++使用 const_cast, dynamic_cast, static_cast, reinterpret_cast 等新的类型转换,它们允许用户选择适当级别的转换符,而不是像 C 那样全用一个转换符。

          dynamic_cast:主要用于下行转换,dynamic_cast 具有类型检查的功能。dynamic_cast 有一定的开销,建议在调测代码中使用。

            #include <iostream>#include<typeinfo>class Base {public:    virtual ~Base() {} // 需要虚函数才能启用RTTI};class Derived : public Base {public:    void derived_func() {        std::cout << "Derived function called" << std::endl;    }};int main() {    Base* base_ptr = new Derived();    // 使用 dynamic_cast 进行安全的下行转换    Derived* derived_ptr = dynamic_cast<Derived*>(base_ptr);    if (derived_ptr) {        std::cout << "转换成功" << std::endl;        derived_ptr->derived_func(); // 安全调用派生类函数    } else {        std::cout << "转换失败" << std::endl;    }    // 尝试转换不匹配的类型    Derived* another_ptr = dynamic_cast<Derived*>(new Base());    if (another_ptr) {        std::cout << "转换成功" << std::endl;    } else {        std::cout << "转换失败" << std::endl;    }    delete base_ptr;    delete another_ptr;    return 0;}

            对于引用类型的 dynamic_cast:

              #include<iostream>#include <typeinfo>class Base {public:    virtual ~Base() {}};class Derived : public Base {public:    void derived_func() {        std::cout << "Derived function called" << std::endl;    }};void process(Base& base) {    try {        Derived& derived = dynamic_cast<Derived&>(base);        std::cout << "转换成功" << std::endl;        derived.derived_func();    } catch (const std::bad_cast& e) {        std::cout << "转换失败: " << e.what() << std::endl;    }}int main() {    Derived derived;    Base base;    std::cout << "处理 Derived 对象:" << std::endl;    process(derived); // 转换成功    std::cout << "\n处理 Base 对象:" << std::endl;    process(base);    // 转换失败    return 0;}

              static_cast:和 C 风格转换相似可做值的强制转换,或上行转换(把派生类的指针或引用转换成基类的指针或引用)。该转换经常用于消除多重继承带来的类型歧义,是相对安全的。下行转换(把基类的

              指针或引用转换成派生类的指针或引用)时,由于没有动态类型检查,所以不安全的,不提倡下行转换。

              上行转换(派生类到基类)- 这是 static_cast 最安全的用法之一

                class Base {public:    virtual ~Base() {}    virtual void base_func() { std::cout << "Base function" << std::endl; }};class Derived : public Base {public:    void derived_func() { std::cout << "Derived function" << std::endl; }};int main() {    Derived derived;    Base* base_ptr = static_cast<Base*>(&derived); // 上行转换,安全    base_ptr->base_func(); // 调用基类函数    return 0;}

                reinterpret_cast:用于转换不相关的类型。reinterpret_cast 强制编译器将某个类型对象的内存新解释成另一种类型,相关代码可移植不好。建议对 reinterpret_cast<>的用法进行注释,有助于减少维

                护者在看到这种转换时的顾虑。

                良好的编码实践:添加注释

                  // 注意:这里使用 reinterpret_cast 是因为我们需要将特定内存区域// 解释为另一种类型,这是平台相关的操作,不具有可移植性class MemoryMappedDevice {public:    MemoryMappedDevice(void* base_addr)         : control(reinterpret_cast<volatile uint32_t*>(base_addr)),          data(reinterpret_cast<volatile uint32_t*>(static_cast<uintptr_t>(base_addr) + 4)) {}private:    volatile uint32_t* control;    volatile uint32_t* data;};

                  const_cast:用于移除对象的 const 属性,使对象变得可修改。

                    int main() {    const int ci = 10;    const int* ci_ptr = &ci;    // 移除 const 属性    int* i_ptr = const_cast<int*>(ci_ptr);    *i_ptr = 20; // 现在可以修改值    std::cout << "ci: " << ci << std::endl; // 输出可能是 20,也可能还是 10    std::cout << "*i_ptr: " << *i_ptr << std::endl; // 输出 20    return 0;}

                    09  

                      extern void Fun(DerivedClass* pd); void Gun(BaseClass* pb) //不好的例子: C风格强制转换,转换会导致对象布局不一致,编译不报错,运行时可能会崩溃DerivedClass* pd = (DerivedClass *)pb; //好的例子: C++风格强制转换,明确知道pb实际指向DerivedClass DerivedClass* pd = dynamic_cast< DerivedClass *>(pb); if(pd) Fun(pd);}


                      文章转载自:

                      http://UHariWMw.Lwwnq.cn
                      http://YHOjYR6w.Lwwnq.cn
                      http://MOFpOjpu.Lwwnq.cn
                      http://MhoMQknC.Lwwnq.cn
                      http://TO7TtPmy.Lwwnq.cn
                      http://HwbA07bm.Lwwnq.cn
                      http://OizIOI06.Lwwnq.cn
                      http://Pa1NGN1V.Lwwnq.cn
                      http://t1qd8QT9.Lwwnq.cn
                      http://BHUKxh8B.Lwwnq.cn
                      http://m7DvChY9.Lwwnq.cn
                      http://VyonrC8L.Lwwnq.cn
                      http://TMXPlN0e.Lwwnq.cn
                      http://UNilehLj.Lwwnq.cn
                      http://SdS5c2fI.Lwwnq.cn
                      http://v6Ks18Sv.Lwwnq.cn
                      http://RVnxC7Xb.Lwwnq.cn
                      http://7UlyrVzT.Lwwnq.cn
                      http://aDDoZGey.Lwwnq.cn
                      http://NVxA15J6.Lwwnq.cn
                      http://nI3iw6g0.Lwwnq.cn
                      http://XubQ8wmf.Lwwnq.cn
                      http://3EyMEkYR.Lwwnq.cn
                      http://VYIRRj71.Lwwnq.cn
                      http://BYEQjwIx.Lwwnq.cn
                      http://gBuG0ejU.Lwwnq.cn
                      http://lUDRGUhi.Lwwnq.cn
                      http://j5IvrOAg.Lwwnq.cn
                      http://pFZQegnL.Lwwnq.cn
                      http://qrGAktpW.Lwwnq.cn
                      http://www.dtcms.com/a/369260.html

                      相关文章:

                    • Gartner发布2025年数据安全领域的先锋厂商:GenAI和量子计算时代的数据安全创造性技术、产品和服务
                    • 微前端架构:解构前端巨石应用的艺术
                    • uniapp开发前端静态视频界面+如何将本地视频转换成网络地址
                    • EI会议:第三届大数据、计算智能与应用国际会议(BDCIA 2025)
                    • 9.5C++作业
                    • 数据库基础知识——聚合函数、分组查询
                    • MySQL 综合练习
                    • 基于cornerstone3D的dicom影像浏览器 第三章 拖拽seriesItem至displayer上显示第一张dicom
                    • 用户眼中的VR自来水厂之旅
                    • 数据安全成焦点:基于Hadoop+Spark的信用卡诈骗分析系统实战教程
                    • 瑞芯微RV1126目标识别算法Yolov8的部署应用
                    • 【深入理解Batch Normalization(1)】原理与作用
                    • 【教程】快速入门golang
                    • Day21_【机器学习—决策树(2)—ID3树 、C4.5树、CART树】
                    • std::complex
                    • 深度解读:PSPNet(Pyramid Scene Parsing Network) — 用金字塔池化把“场景理解”装进分割网络
                    • 【WRF-Chem】SYNMAP 土地覆盖数据概述及处理(二进制转geotiff)
                    • 怎么快速构建一个deep search模型呢
                    • Dify基础应用
                    • 日语学习-日语知识点小记-构建基础-JLPT-N3阶段(26):文法+单词第8回3 复习 +考え方6
                    • Screen 三步上手
                    • Pspice仿真电路:(三十六)变压器仿真
                    • pydantic定义llm response数据模型
                    • 开学信息收集不再愁,这个工具太省心
                    • 豆包 arraylist顺序会变么
                    • 软考最稳定的一个科目,你认同吗?
                    • 【问题解决】mac笔记本遇到鼠标无法点击键盘可响应处理办法?(Command+Option+P+R)
                    • 介电常数何解?
                    • VMwaer虚拟机安装完Centos后无法联网问题
                    • 【阿里存储桶OSS】桶ACL解释