Q_D && Q_Q

Qt 源码中有很多Q_Q和Q_D宏,使用这些宏的地方总会看到有q指针和d指针,查了查KDE文档,大体搞清了其中的机理,欧也!Qt的这些私有数据访问策略还是挺值得借鉴。下面就简单总结一下。

访问器
发了点牢骚,Qt的成员变量get访问器命名实在有点难以接受,get访问器和成员变量名一样,不像Bean的风格,有些编译器甚至通不过。命名的时候就还是加上get好点,习惯成自然,郁闷。

D-指针
私 有成员总是不可见的,Qt中私有成员不仅仅是简单封装一下,将访问权限改为private,它将所有私有数据封装在私有类里(命名就是 classname##private), 这样一来连用户都不知道他到底封装了什么,程序中只有这个私有类成员指针,这个指针就是D-指针。有1毛钱,以前都是把它藏在内衣兜里,现在不是啦,我把 它存在信用卡里,把信用卡藏在内衣兜里,够狠吧。。。

假 如整个类的继承体系有点深怎么办,想用那一层的东西还要一点点的爬着去找,真麻烦! 没关系,有共享d指针呢,只需要在上一层里加个d指针就可以省去这么多麻烦了。它可以访问任意一层的私有数据(其实这样就不是private啦,叫 protected还差不多,有点不像OO),还可以获取父层的访问器。
共享d指针实现方法如下:
1、在基类中定义一个protected权限的d_ptr指针;
2、在每个派生类中定义d_func(),获取基类d_ptr,并将其转换为当前私有类指针(派生自基类d_ptr);
3、在函数中使用Q_D,这样就可以使用d了;
4、在私有数据继承体系中,不要忘记将析构函数定义为虚函数,基类析构函数中释放d_ptr,以防内存泄露!!!
5、类的派生,加上protected 构造函数,调用父类构造函数,将私有数据类传参;

这里有个 共享d指针 的例子。
基类:
protected :
  KFooBasePrivate * const d_ptr ;
  KFooBase ( KFooBasePrivate & dd , QObject * parent );
private :
   friend class KFooBasePrivate ;
   inline KFooBasePrivate * d_func () { return d_ptr ; }
   inline const KFooBasePrivate * d_func () const { return d_ptr ; }

派生类:
protected :
  KFooDerived ( KFooDerivedPrivate & dd , QObject * parent );
private :
   friend class KFooDerivedPrivate ;
   inline KFooDerivedPrivate * d_func ()
   { return reinterpret_cast < KFooDerivedPrivate *>( d_ptr ); }
   inline const KFooDerivedPrivate * d_func () const
   { return reinterpret_cast < KFooDerivedPrivate *>( d_ptr ); }

前两步的声明工作交给Q_DECLARE_PRIVATE宏去处理,就简单多了,我们所要做的就是在私有类的实现和Q_D的使用。

template <typename T> static inline T *qGetPtrHelper(T *ptr) { return ptr; }
template <typename Wrapper> static inline typename Wrapper::pointer qGetPtrHelper(const Wrapper &p) { return p.data(); }

// 在主类中的Private Data 声明,Q_D使用之
#define Q_DECLARE_PRIVATE(Class) /
    inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr)); } /
    inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr)); } /
    friend class Class##Private;

// 在主类中的Private Data 声明(在d指针命名非d_ptr而是Dptr情况下使用),Q_D使用之
#define Q_DECLARE_PRIVATE_D(Dptr, Class) /
    inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(Dptr); } /
    inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(Dptr); } /
    friend class Class##Private;

#define Q_D(Class) Class##Private * const d = d_func()

Q-指针
搞清了d指针,q指针就简单多了,有没有发现’q’和’d’哪里有点像,是滴!’q’就是’d’倒过个来。q指针的方向正好和d指针相反,它是在私有数据类中使用的(d指针只在主类中使用,就是使用私有数据类的那个类,真拗口!),来获取主类指针。

// 在私有数据类中的Public Data声明,Q_Q使用之
#define Q_DECLARE_PUBLIC(Class)                                    /
    inline Class* q_func() { return static_cast<Class *>(q_ptr); } /
    inline const Class* q_func() const { return static_cast<const Class *>(q_ptr); } /
    friend class Class;
#define Q_Q(Class) Class * const q = q_func()


感 觉Qt的这种机制破坏了OO,我想这就是它为何只做内部使用的原因吧。。。   有点完全依赖宏的味道,不像它的signal/slot,通过moc编译插入元数据。不过还是方便hack了。至于signal/slot,我想应该 可以通过macro和函数指针来作为替代方案,改天再see see。这里只是把看到的关于D指针的文章和自己的体会简单总结翻译一下,更详细的可以看下面两个连接。


Reference
http://zchydem.enume.net/2010/01/19/qt-howto-private-classes-and-d-pointers/
http://techbase.kde.org/Policies/Library_Code_Policy#D-Pointers

<think>好的,我现在需要解决用户提出的关于QAC报错的问题。首先,用户提供的代码行是n_missing = have_P && have_Q && have_D && have_E;,其中所有变量都是sint32类型。QAC报错信息指出,一个本质上是布尔类型的表达式在赋值时被转换为有符号类型。 首先,我得理解这个报错的具体原因。用户提到变量都是sint32,也就是有符号32位整数。在C或类似语言中,逻辑运算符(如&&)通常返回布尔值,即0或1。但可能这些变量本身存储的是非布尔值,比如0或非0,导致整个表达式的结果被转换为sint32,而QAC认为这里可能存在类型不匹配的问题。 接下来,我需要考虑用户的实际意图。n_missing可能表示缺少的参数数量,或者是一个标志,检查是否有任何缺失。如果用户想计算缺失的数量,应该用加法;如果是检查是否有任何缺失,应该用逻辑与,但结果可能被隐式转换为整数。 然后,分析可能的解决方案。第一种情况,如果是计算缺失数量,将&&替换为+,每个变量是否为0表示是否存在。第二种情况,如果是检查是否有任何存在,可能需要将结果显式转换为sint32,或者使用条件表达式。此外,显式类型转换或使用布尔到整数的转换方法也可能有效。 还需要考虑QAC的规则,可能要求避免隐式转换,尤其是布尔到整型的转换,因此显式处理会更安全。例如,使用三元运算符或显式赋值给布尔类型再转换。 最后,根据用户的具体需求,提供两种情况的解决方案,并解释哪种情况适用哪种方法,同时强调显式类型转换的重要性以避免警告。</think>该问题的原因及解决方案分步说明如下: ▌问题根源分析 1. 类型本质冲突: - 左侧`n_missing`是sint32整型变量 - 右侧表达式使用逻辑与`&&`运算,在C/C++标准中: * `&&`操作实际产生的是`bool`类型结果(C++)或`int`型0/1值(C) * QAC将其识别为"essentially Boolean"(本质布尔型) 2. 编译器隐式转换: - 当把布尔型结果赋给整型变量时 - 发生`bool`→`sint32`的隐式类型转换 - 违反了MISRA C:2012 Rule 10.5等编码规范 ▌代码意图推测 根据变量命名推测可能有以下两种设计意图: ```c // 情况1:计算缺失参数数量(推荐方案) n_missing = (have_P ? 0 : 1) + (have_Q ? 0 : 1) + (have_D ? 0 : 1) + (have_E ? 0 : 1); // 情况2:检查是否全部参数存在 bool all_exist = have_P && have_Q && have_D && have_E; n_missing = all_exist ? 0 : 1; // 显式类型转换 ``` ▌标准解决方案 根据实际需求选择以下方案: ▶ 方案1:显式类型转换(符合MISRA规范) ```c n_missing = (sint32)(have_P && have_Q && have_D && have_E); ``` ▶ 方案2:布尔转整型标准化写法 ```c n_missing = (have_P && have_Q && have_D && have_E) ? 1 : 0; ``` ▶ 方案3:使用双重否定技巧(常见惯用法) ```c n_missing = !!(have_P && have_Q && have_D && have_E); ``` ▌QAC规则延伸说明 该警告属于: - QA Rule 1.3.1(隐式类型转换) - MISRA C:2012 Rule 10.5(禁止隐式转换) 建议通过添加类型强制转换或重构代码逻辑来明确表达设计意图,这既能消除警告,又能提高代码可读性和类型安全性。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值