Effective C++笔记--面向对象设计

本文探讨了C++编程中常遇到的问题及解决方案,如使用const、枚举和内联函数代替预处理器指令,const成员函数的两种类型,确保对象初始化,避免名称遮掩,区分接口继承与实现继承,考虑虚拟函数之外的选择,避免重新定义继承而来的非虚拟函数及参数等。

摘自:《Effetive C++》中文版第三版

符号表的概念:

从编译器来看,符号表与编译的各个阶段都有交互,一般来讲,符号表有内存地址和函数/变量的对应关系,编译时节点的各种属性(类型,作用域,分配空间大小,(函数)的参数类型等)

1.为什么要用const,enum,inline替换#define

因为例如

#define kkk 1.653

代码段在编译器开始处理源码之前可能会被预处理器取走了,以至于如果在这之后程序中用到了kkk这个常量而且出错时,编译错误信息将会返回1.653而不是kkk。这种情况在多个文件共同编译时而且是有重复定义数值情况下,将无法分辨究竟是哪一个常量出现了编译问题。
而作为一个语言常量,const肯定会被编译器看到,那么一定就会进入符号表,使用const可以避免上述出现多份1.653的情况。
对enum来说,行为会比较像#define而不像const,这三者中只有const变量能够取其地址,enum和#define取地址不合法。
对于形似函数的宏,最好改用inline函数替换#define,因为将函数以宏的形式定义容易出现在预处理器中已经改变参数值的问题。

2.const成员函数bitwise const和logical const

bitwise:成员函数不改变该class的任何成员变量->不改变class内的任何一个bit。
logical:成员函数可以修改class内的某些bits,但是只有客户端监测不楚的情况才行。
const成员函数不能修改普通成员变量,这里是根据bitwise原则来的。但是如果在成员变量前面加上了mutable修饰词,那么const成员函数就可以修改这些普通成员变量了。

3.确定对象被使用前已先被初始化

在之前的编码中,以带参数构造函数例如

NewClass::NewClass(const int& num)
{
    num_ = num;
}

形式,这种不叫作初始化,而叫做类成员函数的赋值,而成员初始化列表才是真儿八经地对成员变量初始化操作。

NewClass::NewClass(const int& num):num_(num){}

区别在于使用赋值操作的构造函数,成员变量是在进入该类的构造函数之后获得的,而C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。

4.避免遮掩继承而来的名称

主要会产生由变量的局部作用域和全局作用域冲突而产生变量覆盖的问题。考虑以下代码:

int x;
void func(){
    double x;
    std::cin>>x;
}

读取这个语句时,编译器将读取输入的double x而不是global int x,因为内层作用域的名称会覆盖外围作用域的名称。

以类的局部作用域为例
class Base{
public:
    virtual void mf1() = 0;
    virtual void mf2();
    void mf3();
...
}

class Derived:public Base{
public:
    virtual void mf1();
    void mf4();
}

若一段代码

void Derived::mf4(){
    mf2();
}

编译器的查找顺序为

  1. mf4覆盖的作用域
  2. Derived覆盖的作用域
  3. Base覆盖的作用域

若Base中也不存在
1. 在Base所属的namespace中查找
2. 在global作用域中查找

5.区分接口继承和实现继承

成员函数的分类:

  • pure virtual
  • impure virtual
  • non virtual
pure virtual
virtual void func() = 0;

为了让派生类只继承函数接口而且必须在派生类中实现该函数

impure virtual

会在基类中提供一份实现代码,派生类可以继承该函数接口和缺省实现,也可以自行重载。这个设计突显出了基类和派生类的共同性质,避免代码的重复。

例如

class Person{
    virtual bool dickcheck(){return true;}
}
class A : public Person{...};
class B : public Person{...};
/*在这种情况下A,B都在调用dickcheck()函数时都将返回true*/

现在出现了一个女人,她继承自Person类

class C : public: Person{....};
Person* human = new C;
human->dickcheck();

这就出现了问题,dickcheck()函数返回为true,但是男女有别。此时class C是无辜的,它并没有说明自己在没有dick的情况下就调用了dickcheck函数,意味着无辜地继承了缺省实现。实际上我们可以将dickcheck函数声明为pure virtual,然后再让每个具有不同特性的派生类单独实现该函数。

class Person{
    public:
        virtual bool dickcheck() = 0;
}
class A : public Person{
    virtual bool dickcheck(){return true;}
}
class B : public Person{
    virtual bool dickcheck(){ return false;}
}

本质上,通过定义我们将dickchenck这个基类中函数划分了N种形态,每种形态对应一个特性的派生类,声明部分成为接口,定义部分表现单个派生类的特性。我们在这里不提供缺省实现是避免某些特殊派生类的加入会使得修改基类代码。

non virtual

声明non-virtual函数是为了让派生类继承接口和一份强制性实现,由于non-virtual代表的是不变性和凌驾特异性,所以它绝不该在derived class中被重新定义。

6.考虑virtual函数以外的其他选择–打破继承体系的限制

Non-virtual Interface手段

通过public的non-virtual函数=PNV调用private的virtual=函数PV。这使得可以在PNV中调用PV的前后执行一些工作,例如互斥变量锁定和系统日志。其实virtual函数不一定得是private,protected的virtual函数使得派生类可以继承基类的PNV调用方式。

std::function实现strategy模式

例如现有基类flower代表一种植物,A中有一个方法void flower1(const flower&)可以让这种植物向着太阳开花,void flower2(const flower&)可以让这种植物向着土地开花。好,
我们写出函数的签名

typedef std::function<void(const flower&)> Flower;

Flower a; //向着太阳开花的植物
Flower b; //向着大地开花的植物
Flower a_f = std::bind(&Flower::flower1, a, _1);
Flower b_f = std::bind(&Flower::flower2, b, _1);
a_f(a);
b_f(b);

7.绝不重新定义继承而来的non-virtual函数

词条在5中已经提到过,这要求我们在设计基类时充分考虑到该类的特性,使得其派生类都无条件继承non-virtual函数时不会产生函数冗余。毕竟non-virtual函数静态绑定,而virtual函数时动态绑定。

8.绝不重新定义继承而来的缺省参数值–因为重新定义了也没用啊…2333

因为缺省参数值是静态绑定,在编译器决定的而不是运行期。

9.通过复合塑模出has-a或“根据某物实现出”

复合就是组合。组合(has-a)和继承(is-a)都是代码复用的方式。
组合:黑盒复用,被包含的对象的内部细节对对外是不可见的。但是为了使得包含另外一个对象的类能够友好地操纵被包含的对象,设计被包含的对象时应该安全合理地考虑对外的接口函数。
继承:白盒复用。派生类会随着基类的改变而改变。

10.private继承和protected继承

private继承不是is-a的关系,而是implemented-in-terms-of,有点类似组合。
- private继承时,编译器不会讲派生类对象转换为基类对象,这意味着在以基类对象为参数的传递时并不能用派生类对象。
- private继承过来的成员在派生类中将全部转变为private属性。

在此处要注意派生类内部和派生类对象的区别。private继承在派生类的子类内部无法访问基类的任何成员。考虑下面的情况:

class Base{
    public:
        int a;
    protected:
        int b;
    private:
        int c;
}
class pri : private Base{
public:
    /*这里不能进行对c进行赋值,因为不论哪种继承方式,派生类均不能对基类的private成员进行操作;*/
    void access(){ a = 1;b=1;}
}
#
pri Pri_1;
//错误,在private继承下,派生类对象不能访问基类的任何成员。
Pri_1.a = 1;
#
/* 派生类的子类对基类成员进行访问*/
class subPri : public pri{
public:
    void access_1(){a = 1; b = 1;}
    //此时派生类子类内部无法访问基类的任何成员,对比public继承,后者的子类仍然可以访问基类的成员。
}

使用情形:当派生类需要访问基类的protected成员或者需要重新定义继承而来的virtual函数时。 —这句话完全不能理解

再列举一下有关protected继承的特性:

class pro : protected Base{
public:
    //正确,在proteced继承的派生类成员内部可以访问基类publicprotected成员
    void access(){ a = 1; b = 1;}
}

pro pro_1;
//错误,在protected继承下,派生类对象无法访问基类的任何成员。
pro_1.a = 1;
空白基类最优化–与组合相比较而言
class Empty{
}

class emptyPri : private Base{
public:
    int x;
}
class composition {
public:
    int x;
    Empty e;
}

当我们用sizeof检查这两个类的大小时,可以发先
sizeof(emptyPri)为4;仅为int的大小
而sizeof(composition)为8

11.多重继承

具有多种最大功率点跟踪(MPPT)方法的光伏发电系统(P&O-增量法-人工神经网络-模糊逻辑控制-粒子群优化)之使用粒子群算法的最大功率点追踪(MPPT)(Simulink仿真实现)内容概要:本文介绍了一个涵盖多个科研领域的综合性MATLAB仿真资源集合,重点聚焦于光伏发电系统中基于粒子群优化(PSO)算法的最大功率点追踪(MPPT)技术的Simulink仿真实现。文档还列举了多种MPPT方法(如P&O、增量电导法、神经网络、模糊逻辑控制等),并展示了该团队在电力系统、智能优化算法、机器学习、路径规划、无人机控制、信号处理等多个方向的技术服务能力与代码实现案例。整体内容以科研仿真为核心,提供大量可复现的Matlab/Simulink模型和优化算法应用实例。; 适合人群:具备一定电力电子、自动控制或新能源背景,熟悉MATLAB/Simulink环境,从事科研或工程仿真的研究生、科研人员及技术人员。; 使用场景及目标:①学习并实现光伏系统中基于粒子群算法的MPPT控制策略;②掌握多种智能优化算法在电力系统与自动化领域的建模与仿真方法;③获取可用于论文复现、项目开发和技术攻关的高质量仿真资源。; 阅读建议:建议结合提供的网盘资料,按照研究方向选取对应模块进行实践,重点关注Simulink模型结构与算法代码逻辑的结合,注重从原理到仿真实现的全过程理解,提升科研建模能力。
热成像人物检测数据集 一、基础信息 数据集名称:热成像人物检测数据集 图片数量: 训练集:424张图片 验证集:121张图片 测试集:61张图片 总计:606张热成像图片 分类类别: - 热成像人物:在热成像图像中的人物实例 - 非热成像人物:在非热成像或普通图像中的人物实例,用于对比分析 标注格式: YOLO格式,包含边界框和类别标签,适用于目标检测任务。数据来源于热成像和视觉图像,覆盖多种场景条件。 二、适用场景 热成像监控与安防系统开发: 数据集支持目标检测任务,帮助构建能够在低光、夜间或恶劣环境下自动检测和定位人物的AI模型,提升监控系统的可靠性和实时响应能力。 红外视觉应用研发: 集成至红外摄像头或热成像设备中,实现实时人物检测功能,应用于安防、军事、救援和工业检测等领域。 学术研究与创新: 支持计算机视觉与热成像技术的交叉研究,助力开发新算法用于人物行为分析或环境适应型检测模型。 教育与培训: 可用于高校或培训机构,作为学习热成像人物检测和AI模型开发的教学资源,提升实践技能。 三、数据集优势 精准标注与多样性: 每张图片均由专业标注员标注,确保边界框定位准确,类别分类清晰。包含热成像和非热成像类别,提供对比数据,增强模型的泛化能力和鲁棒性。 场景实用性强: 数据覆盖多种环境条件,如不同光照和天气,模拟真实世界应用,适用于复杂场景下的人物检测任务。 任务适配性高: YOLO标注格式兼容主流深度学习框架(如YOLOv5、YOLOv8等),可直接加载使用,支持快速模型开发和评估。 应用价值突出: 专注于热成像人物检测,在安防、监控和特殊环境检测中具有重要价值,支持早期预警和高效决策。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值