C++ using使用
400 Words|Read in about 2 Min|本文总阅读量次
源码中出现很多关于私有继承的操作(默认不加public),但是通过using外部可以直接调用,这个是什么原理?
1介绍
(1)父类的public成员在private/protected继承后,在派生类中就成了private/protected权限而不是public权限,子类对象就不能再调用父类的public成员
(2)可能在父类中有100个public成员,在子类对象中只需要访问1个成员,其余99个成员都不需要访问,合适的做法是将100个父类的public成员以private/protected权限继承到子类,然后使用using关键字对需要访问的那一个父类public成员进行权限修改
2demo
2.1使用动态机制突破private权限
通过引用来突破private关键字的方式
1#include <iostream>
2using namespace std;
3
4class A
5{
6public:
7 virtual void func() { cout << "func in A" << endl; }
8};
9
10class B : public A
11{
12private:
13 virtual void func() { cout << "func in B" << endl; }
14};
15
16int main()
17{
18 B b;
19
20 //b.func(); /* 错误:private函数对外不可见 */
21
22 /* 通过虚函数机制,可以突破private的限制,实现对B中的func进行调用 */
23 A &ra = b;
24 ra.func(); /* 输出:func in B */
25}
因为class B继承自class A(严格来说是public继承),所以依据动态绑定规则,class A的引用ra
可以绑定到class B的实例b
上。之后,通过ra
调用func
函数,实际调用到的就是class B中的func
。
2.2当做private来使用
如果把虚继承直接当做一个类内的private字段来操作,那就简单很多了
1#include <iostream>
2#include <string>
3using namespace std;
4
5class A : private string
6{
7public:
8 A() : string("yangyang48") {}
9
10 size_t GetSize1() const
11 {
12 return ((const string *)this)->size();
13 }
14
15 size_t GetSize2() const
16 {
17 return string::size();
18 }
19};
20
21int main()
22{
23 A a;
24 cout << a.GetSize1() << endl; /* 输出: 10 */
25 cout << a.GetSize2() << endl; /* 输出: 10 */
26}
这里说明下,我们操作的string,实际上是basic_string
;
1//string
2using string = basic_string<char>;
3//basic_string.h
4template<typename _CharT, typename _Traits, typename _Alloc>
5class basic_string
6{
7public:
8 size_type
9 size() const _GLIBCXX_NOEXCEPT
10 { return _M_string_length; }
11};
这里是string是一个共有方法的size(),只是用到了私有继承的方式。
实际上上面的两个get方法,本质还是公有函数中调用私有函数,变相增加了两个get方法,但不使用公有get直接调用也是不行的。
领导(string)给马屁精(class A)传达了一些信息(公开了.empty()、.size()、.substr()等public接口),并告诉马屁精:「这些资料,你要原封不动地公开给其它同事(外部调用)
马屁精(class A)转头回到同事中又开始鸡毛当令箭了(将继承方式改为 private继承)。抠抠搜搜地公开了一些信息,还美其名曰“帮大家整理总结”(将size()封装到GetSize1/GetSize2中供外部调用)。
同事(外部调用)一看所谓的“整理总结”,就是糊了个封皮封底(class A公布的接口中,除了无条件调用string的size()接口,别的什么也没做),这个时候同事们(外部调用)不干了,马屁精拍马屁(保持private继承)可以,但能不能别给其他人添乱(不要给接口套层壳,直接让外部调用string的原始接口),马屁精(class A)一看只能用using方式来替换自己的整理,毕竟如果不使用私有继承,自己就没啥用了也不需要using。
C++还确实提供了这种机制:在class A中使用using string::size;
可以直接将string的size()
接口提升为public权限。即外部可以直接调用该接口,形式上等同于针对该接口使用了public继承(string提供的其余接口仍旧是private继承)。
1#include <iostream>
2#include <string>
3using namespace std;
4
5class A : private string
6{
7public:
8 A() : string("yangyang48") {}
9 using string::size;
10};
11
12int main() {
13 A a;
14 cout << a.size() << endl; /* 输出: 10 */
15}
输出结果跟上面一样
110
2.3源码分析
在源码中,也会存在使用using来提升继承成员的访问权限
1//frameworks/native/services/surfaceflinger/Scheduler/Scheduler.h
2namespace android {
3namespace scheduler {
4 class Scheduler : impl::MessageQueue {
5 using Impl = impl::MessageQueue;
6
7 public:
8 Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags);
9 virtual ~Scheduler();
10 ...
11 using Impl::initVsync;
12 using Impl::setInjector;
13 using Impl::getScheduledFrameTime;
14 using Impl::setDuration;
15 using Impl::scheduleFrame;
16 };
17}
18}
19
20namespace android {
21namespace impl {
22
23class MessageQueue : public android::MessageQueue {
24...
25public:
26 explicit MessageQueue(ICompositor&);
27
28 void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
29 std::chrono::nanoseconds workDuration) override;
30 void setDuration(std::chrono::nanoseconds workDuration) override;
31 void setInjector(sp<EventThreadConnection>) override;
32 void waitMessage() override;
33 void postMessage(sp<MessageHandler>&&) override;
34 void scheduleFrame() override;
35 std::optional<Clock::time_point> getScheduledFrameTime() const override;
36};
37}
38}
上述Scheduler使用默认继承impl::MessageQueue,默认继承为private继承,也就是当类的继承方式是私有继承时,基类中的公有和保护成员在派生类中变成私有成员,而基类的私有成员在派生类中不能直接访问。
1//frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
2void SurfaceFlinger::initScheduler(const sp<DisplayDevice>& display) {
3 mScheduler->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(),
4 configs.late.sfWorkDuration);
5}
但是在源码中,发现使用到了initVsync,这个可以直接使用调用到基类impl::MessageQueue中的方法。
往前面看,class Scheduler定义了using Impl::initVsync;这就说明权限不一致了,把using可以修改子类继承自父类的成员权限成public
(1)using可以修改子类继承自父类的成员权限成public,但并不是所有继承自父类的成员都可以修改成public权限;
(2) 如果成员在父类中本来就是private权限,那在子类中是无法使用using声明成public权限的;
总结:using只是用来找回在继承中损失的权限,给部分成员开特例;
3总结
总的来说,使用using可以让私有继承中原本父类的公有方法,在子类中恢复到公有的方法,可以在外部直接调用,这样可以隔离部分私有继承中需要不公开的方法的一种方式。
参考
[1] 正在起飞的蜗牛. 【C++入门】使用using重新定义继承的成员访问权限, 2022.
[2] Renekton_bhk. C++的private并没有听起来那么“保密”(virtual)(using), 2020.