【问题标题】:Give protected access to base class without friend在没有朋友的情况下授予对基类的受保护访问权限
【发布时间】:2014-11-30 07:47:29
【问题描述】:

我先解释一下我的情况。

我有一个自动实现一种引用计数的基类。它允许我将 C 风格的 init()free() 库调用包装到引用计数 API。

template<typename T>
class Service {
public:
    Service() {
        if(s_count++ == 0) {
            T::initialize();
        }
    }

    ~Service() {
        if(--s_count == 0) {
            T::terminate();
        }
    }

private:
    static int s_count;
};

template<typename T>
int Service<T>::s_count = 0;

希望实现这些初始化器和终止器的类将派生自Service,如下所示:

class Test : public Service<Test> {
    friend class Service<Test>;

private:
    static void initialize() {
        std::cout << "Initialized" << std::endl;
    }

    static void terminate() {
        std::cout << "Terminated" << std::endl;
    }
};

但是,声明很混乱,因为我必须继承自 Service 类并成为其好友。 有没有办法让基类自动访问派生类的受保护或私有成员?如果没有,我不妨问问是否有更好的方法来编写我在这里所做的事情。

【问题讨论】:

  • CRTP 使用完全相同的模式。我认为你的代码很好。
  • @RSahu 很好的参考...但是你认为让基类访问派生类的成员是个好主意吗?
  • @ravi,总的来说,答案是否定的。例外是 CRTP,其中基类显式依赖派生类中的函数。
  • 您可能希望将initializeterminate 设为虚拟。
  • @RSahu 应该命名为“好奇调用模板模式”

标签: c++ inheritance


【解决方案1】:

“有没有办法让基类自动访问派生类的受保护或私有成员?”

基类不能正式访问派生类的私有/受保护成员。一般来说,基类的设计方式是它们不需要了解派生类的任何内容。因此,如果需要从您的基类访问派生类中的成员,那么您应该重新考虑您的设计。

编辑(根据@RSahu 提出的文章):-

虽然在某些情况下从基类访问派生类的成员函数可能很有用。就像您在两个进程之间共享对象一样。

【讨论】:

    【解决方案2】:
    #include <iostream>
    using namespace std;
    
    template<typename T>
    class Service {
        struct TT: T {
          using T::initialize;
          using T::terminate;
        };
    public:
        Service() {
            if(s_count++ == 0) {
                TT::initialize();
            }
        }
    
        ~Service() {
            if(--s_count == 0) {
                TT::terminate();
            }
        }
    
    private:
        static int s_count;
    };
    
    
    class Test : public Service<Test> {
        //friend class Service<Test>;
    
    protected:
        static void initialize() {
            std::cout << "Initialized" << std::endl;
        }
    
        static void terminate() {
            std::cout << "Terminated" << std::endl;
        }
    };
    
    template<typename T>
    int Service<T>::s_count = 0;
    
    
    int main() {   
        Test t;
    }
    

    【讨论】:

      【解决方案3】:

      n.m. 将这些方法设为虚拟的建议让我想到:它自己不会工作,但如果您将服务与其管理分离,它工作:初始化不仅仅适用对于那个特定的服务实例,它适用于所有实例,也许正因为如此,它一开始就不应该是服务类的一部分。

      如果将它们解耦,则可以创建一个服务管理器基类,其中包含派生服务管理器必须实现的虚拟方法,如下所示:

      #include <iostream>
      
      class ServiceManager {
        template <typename T>
        friend class Service;
        virtual void initialize() = 0;
        virtual void terminate() = 0;
      };
      
      template <typename T>
      class Service {
      public:
        Service() {
          if (s_count++ == 0) {
            s_manager.initialize();
          }
        }
        ~Service() {
          if (--s_count == 0) {
            s_manager.terminate();
          }
        }
      
      private:
        static int s_count;
        static ServiceManager &&s_manager;
      };
      
      template <typename T>
      int Service<T>::s_count = 0;
      
      template <typename T>
      ServiceManager &&Service<T>::s_manager = T();
      
      class TestManager : public ServiceManager {
        void initialize() {
          std::cout << "Initialized" << std::endl;
        }
        void terminate() {
          std::cout << "Terminated" << std::endl;
        }
      };
      
      class Test : public Service<TestManager> {
      };
      

      如果您的编译器不支持 &amp;&amp; 的这种使用(它是有效的 C++11,但不是有效的 C++03),您应该仍然可以通过将 s_manager @ 987654324@ 并且不使用临时的T 来初始化它,或者只是使s_manager 具有T 类型。前者更冗长,后者允许不从ServiceManager 派生的T 实现。

      【讨论】:

        猜你喜欢
        • 2017-08-22
        • 2012-07-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多