C++ using - 繼承共同行為 & 改變成員的訪問權限

前言

PCL中的surface/include/pcl/surface/mls.h裡宣告的pcl::MovingLeastSquares類別有這麼一段代碼:

namespace pcl
{
  template <typename PointInT, typename PointOutT>
  class MovingLeastSquares : public CloudSurfaceProcessing<PointInT, PointOutT>
  {
    public:
      //...
  
      using PCLBase<PointInT>::input_;
      using PCLBase<PointInT>::indices_;
      using PCLBase<PointInT>::fake_indices_;
      using PCLBase<PointInT>::initCompute;
      using PCLBase<PointInT>::deinitCompute;
      //...
  };
}

這邊的using有:

  1. 繼承共同行為

  2. 改變成員的訪問權限

兩種作用,詳見下文。

繼承關係

要看懂這邊這麼寫的用意,得先了解一下MovingLeastSquares類別的來歷,以下是這個類別的繼承關係:MovingLeastSquares → \rarr CloudSurfaceProcessing → \rarr PCLBase

PCLBase類別中有protected成員變數input_,indices_,fake_indices_及public成員函數initCompute,deinitCompute

namespace pcl
{
  //...
  template <typename PointT>
  class PCLBase
  {
    public:
      //...

    protected:
      /** \brief The input point cloud dataset. */
      PointCloudConstPtr input_;

      /** \brief A pointer to the vector of point indices to use. */
      IndicesPtr indices_;

      /** \brief Set to true if point indices are used. */
      bool use_indices_;

      /** \brief If no set of indices are given, we construct a set of fake indices that mimic the input PointCloud. */
      bool fake_indices_;

      /** \brief This method should get called before starting the actual computation.
        *
        * Internally, initCompute() does the following:
        *   - checks if an input dataset is given, and returns false otherwise
        *   - checks whether a set of input indices has been given. Returns true if yes.
        *   - if no input indices have been given, a fake set is created, which will be used until:
        *     - either a new set is given via setIndices(), or
        *     - a new cloud is given that has a different set of points. This will trigger an update on the set of fake indices
        */
      bool
      initCompute ();

      /** \brief This method should get called after finishing the actual computation.
        */
      bool
      deinitCompute ();

    public:
      PCL_MAKE_ALIGNED_OPERATOR_NEW
  };

CloudSurfaceProcessing類別繼承自PCLBase,它有public成員變數input_,indices_以及public成員函數initCompute,deinitCompute

namespace pcl
{
  //...
  template <typename PointInT, typename PointOutT>
  class CloudSurfaceProcessing : public PCLBase<PointInT>
  {
    public:
      //...

      using PCLBase<PointInT>::input_;
      using PCLBase<PointInT>::indices_;
      using PCLBase<PointInT>::initCompute;
      using PCLBase<PointInT>::deinitCompute;
      
      //...
  };
}

讓變數/函數變成dependent name

參考Why do I have to access template base class members through the this pointer?(巨著,有條件的最好都看一下這篇回答):

the standard in effect says that dependent base classes of class templates just aren't considered for search unless explicitly requested. This stops everything from being dependent just because it could be found in a dependent base. It also has the undesirable effect that you're seeing - you have to qualify stuff from the base class or it's not found

編譯器在編譯一個derived class template時,不會去尋找base class template。

看看CloudSurfaceProcessing這個例子,它是一個繼承自PCLBase<PointInT>的derivated class template。在默認情況下,編譯器如果看到CloudSurfaceProcessing的代碼中用了input_,並不會去它的base class template,也就是PCLBase<PointInT>中查找,所以input_在這種情況下並非dependent name。

編譯器會直接把它當成一個未宣告的變數,造成以下編譯錯誤:

Error	C2065	'transformation_epsilon_': undeclared identifier

或:

Error	C3861	'transformation_epsilon_': identifier not found
There are three common ways to make A dependent:

using Bar<T>::A; in the class - A now refers to something in Bar<T>, hence dependent.
Bar<T>::A *x = 0; at point of use - Again, A is definitely in Bar<T>. This is multiplication since typename wasn't used, so possibly a bad example, but we'll have to wait until instantiation to find out whether operator*(Bar<T>::A, x) returns an rvalue. Who knows, maybe it does...
this->A; at point of use - A is a member, so if it's not in Foo, it must be in the base class, again the standard says this makes it dependent.

要讓input_變成dependent name(即讓編譯器知道它是繼承而來)有以下三種方法:

this->A;
int x = Bar<T>::A; // 假設Bar<T>::A是int型別
using Bar<T>::A;

第一種是每次用A時都要在前面加this->,這顯然不是一個好方法。
第二種是則是在A前面加Bar<T>::A,比第一種方法更麻煩。
第三種則是一勞永逸的做法:寫一次using Bar<T>::A;,之後在任何地方都可以直接用A而不造成編譯錯誤。

繼承共同行為

參考繼承共同行為

MovingLeastSquaresPCLBase這兩個類別都有input_, indices_, fake_indices_, initComputedeinitCompute,這是他們的“共同行為”。

這裡的using指定了MovingLeastSquares要使用PCLBase中的,而非CloudSurfaceProcessing中的。

改變成員的訪問權限

參考使用 using

PCLBase中,這些成員變量/函數本來是protected。這種寫法同時也將繼承的成員導入到public權限區塊,
代表input_, indices_, fake_indices_, initComputedeinitCompute都變為MovingLeastSquarespublic成員。

範例程序

#include <iostream>

class Shape{
protected:
    float area;
public:
    void printArea() {
        std::cout << "I'm Shape with area = " << area << std::endl;
    }
};

//note: public inheritance
class Rectangle : public Shape{
protected:
    using Shape::area;
public:
    void printArea() {
        std::cout << "I'm Rectangle with area = " << area << std::endl;
    }

};

class Square : public Rectangle{
public:
    using Shape::area;
    //note: no () after function name
    using Shape::printArea;
};

int main(){
    Square sqr;
    //"area" becomes a public member of "Square"
    sqr.area = 9;
    //it uses "printArea" from "Shape"
    sqr.printArea();
    //I'm Shape with area = 9

    return 0;
}

在上面的例子中,可以看出:

  1. 在main函數裡面可以直接修改Square物件的area成員函數,可見area已經由protected變為public
  2. sqr.printArea()函數的行為與Shape中定義的printArea一致。這代表SquareprintArea是繼承自Shape而非Rectangle

以上程序放在using_keyword.cpp

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值