【问题标题】:Giving derived class only const access to base class data members/functions仅授予派生类对基类数据成员/函数的 const 访问权限
【发布时间】:2016-01-15 19:40:21
【问题描述】:

在我的具体情况下,我有一个基类“Base”,其中有一个数据成员“A_var”。我希望任何派生类都只能以 const 访问该数据成员,在语法上与“Base”类相同。

如果它是受保护的或私有的,则派生类分别具有完全访问权限或无访问权限。我可以将其设为私有,并创建一个返回 const 引用的受保护函数,但是访问在语法上会有所不同。

class Base {
protected:
    const type_t& A() const {return A_var;}
private:
    type_t A_var;
    void f();
};

class Derived : public Base{
public:
    void g();
};

//access in Base class
void Base::f() {
    type_t value = A_var;
    A_var = value;
}

//access in Derived class
void Derived::g() {
    type_t value = A();
    A() = value; //Error, const reference; good
}

重载 'A()',如下所示,也不起作用,因为 'Derived' 类调用私有非 const 'A()'。

protected:
    const type_t& A() const {return A_var;}
private:
    type_t& A() {return A_var;}

微小的差异可能看起来没什么大不了的,但在我的代码中,有各种宏以访问该数据成员开始。因此,我必须为“基”类和派生类设置不同的宏,这会破坏代码的读取和写入流程。

更新: 澄清一下,问题在于使派生类和基类中的访问权限相同,语法。也就是说,例如,我可以调用一个函数 f(),并让它在基类中调用时返回一个非常量引用,但在派生类中调用时返回一个 const 引用。动机是使派生类中的强制 const 访问无缝。我意识到可能没有办法做到这一点,但我问以防万一。

更新: 举一个真实的例子(这样的案例有2-3个),在代码中使用了很多:

 test_files_var.current()->current_test()

我把它换成了

 #define TEST() test_files_var.current()->current_test()

因为派生类将通过不同的函数/成员(即testFiles())访问test_files_var,所以我必须对TEST() 进行第二个定义,即DTEST()。这个问题更多地取决于“宏”的使用次数,而不是它们的数量。

【问题讨论】:

  • “但在我的代码中有各种宏......” 这可能是严重设计缺陷的根源。避免使用宏。
  • 这不会违反Liskov substitution principle吗?
  • @πάνταῥεῖ 也许你是对的。但是,它们并不是很长的宏,并且“各种”可能正在推动它。举一个真实的例子(其中有 2-3 个),这在代码“test_files_var.current()->current_test()”中被大量使用。我用#define TEST() test_files_var.current()->current_test() 替换了它。因为派生类将通过函数访问 test_files_var,即 testFiles(),所以我必须有 TEST() 的第二个定义,即 DTEST()。这个问题更多地取决于“宏”的使用次数,而不是它们的数量。
  • @VladFeinstein 我刚刚快速阅读了 Liskov 替代原理的定义,所以我的回答持保留态度,但我认为不是。 Base 类甚至是抽象的,派生类中没有接口函数。
  • @Ramon 您可能应该在您的问题帖子中介绍这些附加信息(而不是 cmets),以便更清楚您的实际用例是什么。

标签: c++


【解决方案1】:

没有简单的内置解决方案。 但是一点模板魔术可能会起到作用:

template <class NonConst>
struct Matcher {
    template <class AnyOther>
    static const AnyOther &get(AnyOther &obj) { return obj; }

    static NonConst &get(NonConst &obj) { return obj; }
};

class Base {
public:
    Base() : a_(42) { }
public:
    virtual void Fun() {
        Matcher<Base>::get(*this).A();
    }

    const int &A() const {
        std::cout << "const" << std::endl;
        return a_;
    }
    int &A() {
        std::cout << "no const" << std::endl;
        return a_;
    }
private:
    int a_;
};

class Derived : public Base {
public:
    void Fun() {
        Matcher<Base>::get(*this).A();
    }
};

int main(int argc, const char * argv[]) {
    Derived d;
    d.Fun();

    Base b;
    b.Fun();
    return 0;
}

上面的代码会输出:const no const

因此,在两个Fun 函数中,您具有基本相同的访问模式,如果需要,您可以将其封装在宏中。

【讨论】:

  • 效果很好,谢谢!为了完整起见,A() 的 const 重载是受保护的,非 const 是私有的。
【解决方案2】:

如果我正确理解了您的问题,您希望对派生类的访问权限授予对基类的私有变量的访问权限,但只能以只读方式访问。

在这种情况下,您只需定义一个受保护的常量引用变量并将其初始化为私有变量:

class Base {
public: 
    Base() : cA(A_var) { ... }  // to be completed with rule of 3
protected:
    const type_t& cA;
private:
    type_t A_var;
    void f();
};

派生类中的访问使用then常量引用:

//access in Derived class
void Derived::g() {
    type_t value = cA;
    //cA = value; //Error, const reference: can't assign 
}

Live demo

【讨论】:

  • 这提供了所需的语法,我仍然想指出这种方法的一个缺点:派生类中的引用需要存储,通常是指针的大小。这可能无关紧要,但仍然值得考虑。
  • @UlrichEckhardt 在这种情况下,在基类中使用A_var 来访问它,在派生类中使用cA。请参阅帖子上的更新,了解为什么它对我的情况有影响。感谢您提供有关存储开销的信息;这与我的情况无关。
【解决方案3】:

您可以将 Base 类更改为

class Base {
public:
    Base() : A_cref(A_var) {}
private:
    type_t A_var;
    void f();
protected:
    const type_t& A_cref;
};

额外的成员开销。

【讨论】:

    【解决方案4】:

    如果宏可以从派生类中使用,它们只需要 const 访问 - 因此宏可以使用受保护的 const 访问函数。

    要修改变量的宏必须使用私有变量,并且只能在基类中使用。

    【讨论】:

      【解决方案5】:

      如果您将 TEST() 宏替换为基类中的公共函数 test(),您的问题似乎就会消失:

      class Base
      {
      public:
          void test() { test_files_var.current()->current_test() }
      private:
          type_t test_files_var;
      };
      

      【讨论】:

        猜你喜欢
        • 2011-03-07
        • 2018-09-18
        • 1970-01-01
        相关资源
        最近更新 更多