基类的friend类如何通过从基类派生的类的对象来访问该基类的成员?

时间:2016-07-13 06:48:56

标签: c++ inheritance language-lawyer friend

以下是我在源文件source.cpp中的代码:

class B
{
  friend class F;
protected:
  int protectedIntB;
};

class D : public B {};

class F
{
public:
  int f(D &d) {return ++d.protectedIntB;}
};

当我使用g++ -c -Wall -pedantic -std=c++11 source.cppcl /c source.cpp编译上述代码时,两个编译器都成功编译。但是,当我使用protected代替public而使B继承自B时

class D : protected B {};

这一次,gcc成功编译,而cl给出错误,表示B::protectedIntBreturn ++d.protectedIntB;无法访问。

另一种情况是将public替换为private

class D : private B {};

这次,两个编译器都会产生错误。顺便说一下,我使用mingw-w64构建的gcc 5.3.0版本和VS2015的cl版本19.00.24210。

我的问题出现了:

基类的友元类如何通过从基类派生的类的对象访问该基类的成员,以及为什么gcc和cl以不同的方式处理它?<​​/ p>

修改

感谢songyuanyaoBrian,这似乎是protected案例中gcc 5.3.0中的错误。只有public案例才能成功编译,gcc 6.1.0也可以正常编译。

3 个答案:

答案 0 :(得分:3)

根据[class.access.base] / 5:

  

对会员的访问受到影响   由成员命名的类。此命名类是成员名称所在的类   抬头找到了。

根据[class.access.base] / 6:

  

如果使用类成员访问运算符(包括隐式“this->”)来访问非静态数据成员   或非静态成员函数,如果左操作数(被认为是指针中的指针),则引用是错误的   “.”运算符case)不能隐式转换为指向右操作数命名类的指针。

因此,在您的特定情况下,为了访问d.protectedIntB,必须满足以下两个条件:

  • 您必须有protectedIntB成员B才能访问B,因为protectedIntB是找到名称B的类。 (注意:这可以通过使用 using-declaration; 重新声明派生类中的成员来改变,在这种情况下派生类将控制它。)

  • 您必须有权D作为D*的基础,能够将B*转换为B }。如果DB的公共基础,那很好。如果DF::f的受保护基础,则会应用访问权限检查,F失败,因为D不是D的朋友且不是派生的等级 public class discount { int quantity; double price; public static int NINETY_NINE = 99; public static int TWENTY = 20; private static int TEN = 10, FORTY_NINE = 49; public static double TEN_PERCENT = 0.10, FIVE_PERCENT = 0.05, TWO_PERCENT = 0.02, THREE_PERCENT = 0.03, ONE_PERCENT = 0.01 ; private double discount; // public static double discount_amount = discount * quantity * price; discount(int quantity, double price) { double discount = 0; //double d = discount; this.quantity = quantity; this.price = price; } boolean quantityOutOfRange() { return quantity < 0; } boolean priceOutOfRange() { return price < 0; } public double getDiscount() { return discount; } public int getQuantity() { return quantity; } public double getUnitPrice() { return price; } public void calculate() { if (quantity > NINETY_NINE) { if (price > TWENTY) discount = TEN_PERCENT; else if ( price > TEN) discount = FIVE_PERCENT; else discount = TWO_PERCENT; } else if (quantity > FORTY_NINE) { // calculate discount if (price > TWENTY) discount = THREE_PERCENT; else if ( price > TEN) discount = TWO_PERCENT; else discount = ONE_PERCENT; } else { // calculate discount if (price > TWENTY) discount = TWO_PERCENT; else if ( price > TEN) discount = ONE_PERCENT; else discount = 1; } if (quantity < 0) { System.out.println("Quantity is negative. Cannot compute discount"); } //double discount_amount = discount * quantity * price; } } public class TestDiscount { public static void main(String[] arg) { String input = JOptionPane.showInputDialog("Enter the quantity desired " + ", and unit price " + "\n(Separated by spaces)"); Scanner in = new Scanner(input); int quantity = in.nextInt(); double price = in.nextDouble(); discount current = new discount(quantity, price); current.calculate(); System.out.println("\nDiscounts:\n"); System.out.println("The quantity is: " + current.getQuantity()+ "\tThe unit price is = $ " + current.getUnitPrice() + "\tThe discount is = $ " + current.getQuantity()* current.getUnitPrice() * current.getDiscount()); System.exit(0) ; } }

令人惊讶的是,GCC似乎是编译器在受保护的情况下是错误的但是这个错误appears fixed。请注意,Clang提供much better diagnostic

答案 1 :(得分:3)

如果使D继承自B使用protected或private而不是public,则编译应该失败。

从标准$11.2/5 Accessibility of base classes and base class members [class.access.base]

开始
  

如果

,则在N类中命名时,可以在R点访问成员m      

(5.4)存在可在R处访问的N的基类B.   当在B组中命名时,可以在R访问m。[例如:

class B;
class A {
private:
  int i;
  friend void f(B*);
};
class B : public A { };
void f(B* p) {
  p->i = 1;         // OK: B* can be implicitly converted to A*,
                    // and f has access to i in A
}
     

- 结束示例]

对于第一种情况,B的基类D可在F::f()访问,因为它是公共继承。 B::protectedIntB可以访问F::f(),因为它是班级B的朋友。

如果将其更改为受保护或私有继承,则B的基类D无法再次在F::f()访问,则编译将失败。注意F::f()不是派生类D的朋友。这意味着如果你把它作为类D的朋友,编译就会成功。

BTW:我尝试使用gcc here进行受保护的继承,但失败了。

答案 2 :(得分:0)

如果代码在gcc 5.3.0上编译并且不在cl上编译,则概率很高,其中一个不严格执行C ++标准。如果我必须猜测,对于受保护和私有继承,您应该得到编译器错误。有关不同类型的继承之间的差异,请参阅Difference between private, public, and protected inheritance以获取更多详细信息。

为了使F类能够访问B类的私有成员,它应该知道D类派生自B类,它只会发生在公共继承中。