private 继承 真的没用吗??

本文探讨了C++中的private继承并非无用,它表达的是"以某物来实现"的关系,而非"is a"。private继承使得基类的成员在派生类中变为private,适用于实现细节的继承而不公开接口。文章通过举例说明了private继承与复合的区别,建议在可以使用复合的情况下避免private继承,但强调在特定场景下,如需要修改基类虚函数或考虑对象大小优化时,private继承是必要的选择。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

private继承,在看到effective C++之前,我发现在我之前的代码里面,没有用到这个机制,但是认真回想起还是有一些影子。

大多数人认为private继承是没有用的,其实不然,在某些情况下,它也会带给你一些惊喜。


好,言归正传,之前已经数次提到过 public继承表示的是  “is   a ”的关系,也就是说每一个derived对象D同时也是base对象,任何在base对象上的操作可以实施到derived对象身上。


例如

class Person{....};
class Student::public Person{...};

Person p;
Student s;

void eat(Person *p);
void study(Student &s);

eat(s);//可以施加到Person身上的操作,对于Student对象仍然使用。
eat(p); 


但是,如果将上述的public继承改为private继承,eat(s) 调用将会失败。


那么,private继承到底意味着什么,首先很直观的讲,上述的Student对象并没有转化为Person对象,其次,由

private继承而来的所有Base的成员在Derived中均是private属性的(无论其原先在Base类中的属性是public或者是

protected)。


最重要的一点,private继承意味着 “ is   implemented as ...” (以某物来实现),也就意味着,Derived类只是继承

了Base类中的属性,并未继承其接口。这是实现层面上的事儿。


值得注意的是,复合 的意义也是  “ is   implemented as ...” (以某物来实现),这似乎还是印证了那句 “private继承无用”。


关于此二者的权衡,很简单,在两者均可以的情况下,选择使用 复合,而不是private继承, 除非在一些很特殊的情况,接下来的几

个例子就是用来讲二者的区别。


假设:

我们需要定义一个类 Timer 来统计当前类 Widget 所处的状态,例如函数调用次数等,那么我们需要一个这样的类来实现这样的功能。

定义可以如下:


class Timer{
public: explicit Timer(int ticks);
virtual ontick() const;//需要计数的类继承此函数,
...
};


显然,在Widget类中需要使用Timer类的对象,采用 public 继承显然是不合适的,因为两者没有从属关系。那么想到了private继承。

并且重定义了ontick函数。


class Widget:public Timer
{
private:
virtual ontick() const;
...
};


注:此处将ontick函数定义在private部分,是为了不让类的对象误认为可以调用ontick函数(该函数为private, 无论定义在什么位置)。


显然,这是可行的,那么使用复合难道就不可以了 吗?显然是可以的,并且鼓励使用复合,只需要在Widget的内部定义一个值属于该类自身的一个类用来继承Timer类,即:


class Widget
{
    private:
        class TimerManager:public Timer
        {
            public:
                void ontick() const;
                ...
        };
        TimerManager timer;
        ...

};

这个设计比上述的private继承更易理解。还有一点很有意思,在此处假设 Widget 是意欲成为Base class的类,也就

是说其他的类会继承Widget 如果你使用private继承的话,那么Widget 的derived 类可能会修改了ontick 函数(毕竟这

也是它的基类的虚函数)。

但是,如果需要在该函数中重写Timer中的virtual函数,那么选择private继承就是必须的了。


次,如果是 private 继承,在定义Widget 类的时候,Timer类必须是可见的,也就是说必须有包含 “Timer.h” 字样,

可是在复合中,完全可以使用 Pointer to  Implement  (pImpl)技术,在Widget类定义一个TImer的指针即可,这里就

只需要Timer的声明,从而降低了编译的耦合性


但是对于如下的极端例子:要继承的类是空类


class Empty{};
class HasAInt
{
    private:
        int a;
        Empty e;
};

上述的   sizeof( hasAInt ) > sizeof( int ) ; 对于空类占用空间的说明见 http://blog.youkuaiyun.com/xuqingict/article/details/24433311


但是使用private继承。C++编译器便有一种称作  “ 空白基类最优化的” 原则,是的sizeof(hasAInt) == sizeof(int);

(注:此原则只对单一继承有效),这对于 那些对对象尺寸要求很严格的程序来说,可能很重要。



综上所述:private继承不是没有用的,但是如果可以采用复合实现相同的功能,那就对它敬而远之吧。











<think>好的,用户现在的问题是关于如何在MyBatis Plus中使用BasePageForm中的sortField和sortOrder字段来实现前端传参并进行排序。首先,我需要确认用户的需求。他们有一个公共的分页类BasePageForm,里面包含了pageNum、pageSize、sortField和sortOrder这四个字段,但目前这两个排序字段还没用上。用户希望知道如何在MyBatis Plus中利用这两个字段,让前端可以传递排序参数,并在查询时应用这些排序规则。 接下来,我需要回忆MyBatis Plus的分页查询机制。通常,MyBatis Plus的分页查询使用Page对象,其中可以设置当前页码、每页大小以及排序条件。Page对象有一个addOrder方法,可以添加排序条件,比如根据某个字段升序或降序排列。 用户的问题是如何将前端传来的sortField和sortOrder动态转换成Page对象的排序条件。首先,需要确定前端传递的参数结构。通常,前端可能会传递sortField为数据库字段名,sortOrder可能为"asc"或"desc"表示排序顺序。 然后,在后端处理时,当接收到PerformanceRankQueryForm对象时,其中包含了继承自BasePageForm的sortField和sortOrder。需要将这些参数转换为MyBatis Plus的排序条件。可能需要在构建Page对象时,根据这两个字段的值添加排序条件。 可能的步骤包括: 1. 在Controller层获取到PerformanceRankQueryForm对象。 2. 创建Page对象,并设置pageNum和pageSize。 3. 检查sortField和sortOrder是否有值,如果有的话,将其转换为相应的排序条件,并添加到Page对象中。 4. 使用MyBatis Plus的page方法进行分页查询,并将Page对象传递进去。 需要注意的是,可能存在潜在的安全问题,比如SQL注入,因为前端传来的字段名直接用于排序。因此需要对sortField进行校验,确保其是合法的数据库字段,避免恶意用户传入非法字段导致攻击。 另外,如何将sortField和sortOrder转换为正确的排序方式。例如,如果sortOrder是"desc",则需要降序排列,否则升序。可以使用MyBatis Plus提供的OrderItem类来构建排序条件。 接下来,可能需要编写一个工具方法,将BasePageForm转换为Page对象,并处理排序逻辑。例如: public Page convertToPage(BasePageForm form) { Page page = new Page(form.getPageNum(), form.getPageSize()); if (StringUtils.isNotBlank(form.getSortField()) && StringUtils.isNotBlank(form.getSortOrder())) { if ("asc".equalsIgnoreCase(form.getSortOrder())) { page.addOrder(OrderItem.asc(form.getSortField())); } else { page.addOrder(OrderItem.desc(form.getSortField())); } } return page; } 但需要注意,这里的字段名是否需要使用lambda表达式或者直接使用字符串。如果使用字符串,可能会有字段名错误的风险,所以最好有校验机制。 另外,用户可能希望这是一个公共方法,可以适用于不同的查询表单。因此,可能需要将处理分页和排序的逻辑封装成一个通用的方法,接受BasePageForm参数,并返回配置好的Page对象。 总结步骤: 1. 在Controller中接收PerformanceRankQueryForm。 2. 创建Page对象,并设置分页参数。 3. 根据sortField和sortOrder添加排序条件。 4. 调用MyBatis Plus的分页查询方法,传入Page对象。 5. 返回结果给前端。 可能还需要处理字段名的校验,比如使用MyBatis Plus的自动映射或者自定义校验逻辑,确保前端传来的字段名是允许的。 现在,用户可能遇到的常见问题包括:如何正确传递字段名,如何处理大小写,如何防止SQL注入,以及如何处理不同的排序顺序参数(比如前端可能传递ascending/descending,而不是asc/desc)。 因此,在回答时,需要给出一个具体的代码示例,展示如何将BasePageForm转换为Page对象,并处理排序逻辑,同时提醒用户进行字段校验和安全处理。</think>在MyBatis Plus中可以通过以下方式实现动态排序: ```java // Controller层示例 public PageResult queryPerformanceRank(@RequestBody PerformanceRankQueryForm form) { // 1. 创建分页对象 Page<PerformanceRank> page = new Page<>(form.getPageNum(), form.getPageSize()); // 2. 处理排序逻辑 if (StringUtils.isNotBlank(form.getSortField()) && StringUtils.isNotBlank(form.getSortOrder())) { // 3. 校验排序字段(防止SQL注入) if (isValidSortField(form.getSortField())) { // 4. 添加排序条件 if ("desc".equalsIgnoreCase(form.getSortOrder())) { page.addOrder(OrderItem.desc(form.getSortField())); } else { page.addOrder(OrderItem.asc(form.getSortField())); } } } // 5. 执行查询 Page<PerformanceRank> result = performanceRankMapper.selectPage(page, null); return convertToPageResult(result); } // 排序字段校验方法(示例) private boolean isValidSortField(String field) { // 实际应使用白名单校验合法字段 Set<String> allowedFields = new HashSet<>(Arrays.asList( "assess_year_month", "site_id", "assess_line_code")); return allowedFields.contains(field.toLowerCase()); } ``` 实现要点说明: 1. 使用`Page.addOrder()`方法动态添加排序条件 2. 必须对前端传入的字段名进行白名单校验 3. 支持多字段排序时可以循环处理多个排序条件 4. 排序方向判断建议统一转换为小写处理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值