C++中public、protected以及private的使用


相比C语言,C++中通过class/struct来定义既包含数据,又包含行为的结构,从而支持了“对象”。现实世界中,一个人(一个对象)通常 拥有一些资产(数据),并且掌握某些技能(行为),并且这些资产和技能通常可以分为三类:

  1. 可以与任何人分享的
  2. 有限分享的,比如留给子孙后代的财产或本领
  3. 除了自己之外谁也不能用的,比如给自己留的棺材^_^

为了表达类似的概念,在C++中使用public、protected以及private,分别代表可任意分享的、有限分享的以及独享的。比现实世界稍微复杂些,在C++中这三个关键字不仅可以修饰类成员,还可以修饰类的继承关系。

当这三个关键字用在类成员时:

class Base
{
 public:
  void publicMethod()
  {
    cout << "Begin of " << __FUNCTION__ << endl;
    cout << "End of " << __FUNCTION__ << endl;
  }

  static void staticPublicMethod(Base* obj)
  {
    // just call this class's static private method
    staticPrivateMethod(obj);
  }

  int getProperty() { return property_; }
 protected:
  void protectedMethod()
  {
    cout << "Begin of " << __FUNCTION__ << endl;
    cout << "call publicMethod from protected method: " << endl;
    publicMethod();
    cout << "End of " << __FUNCTION__ << endl;
  }
 private:
  void privateMethod()
  {
    cout << "Begin of " << __FUNCTION__ << endl;

    cout << "call publicMethod from private method: " << endl;
    publicMethod();

    cout << "call protected method from private method: " << endl;
    protectedMethod();

    cout << "End of " << __FUNCTION__ << endl;
  }

  static void staticPrivateMethod(Base* obj)
  {
    // ok
    obj->publicMethod();

    // ok
    obj->protectedMethod();

    // ok
    obj->privateMethod();
  }

  int property_;
};

class Derived : public Base
{
 public:
  void accessBase()
  {
    cout << "In Derived::" << __FUNCTION__ << ": " << endl;
    cout << "we can access Base's public method:" << endl;
    publicMethod();

    cout << "we can access Base's protected method too: " << endl;
    protectedMethod();

    cout << "but we can't access Base's private method!" << endl;
    // we will get a compile error if we try to call privateMethod: 'privateMethod' is a private member of 'Base'
    // privateMethod();
  }

  static void staticDerivedMethod(Derived* obj)
  {
    // ok
    obj->protectedMethod();

    // compile error: 'privateMethod' is a private member of 'Base'
    // obj->privateMethod();
  }
};

int main()
{
  Base obj;
  // ok
  obj.publicMethod();

  // compile error: 'protectedMethod' is a protected member of 'Base'
  // obj.protectedMethod();

  // compile error: 'privateMethod' is a private member of 'Base'
  // obj.privateMethod();

  // ok
  Base::staticPublicMethod(&obj);

  Derived dobj;
  // ok
  dobj.accessBase();

  // ok
  Derived::staticDerivedMethod(&dobj);
  return 0;
}

从上面的程序中,可以看出:

  1. 用public修饰的成员,既可以在其类内部使用(在privateMethod中调用),也可以在类外部通过类的实例来访问(main函数中调用obj.publicMethod())
  2. 用protected修饰的成员,能在该类内部或者其派生类(严格上说是非私有继承的派生类,下面会详细介绍)中使用(见privateMethod、accessBase以及staticDerivedMethod)
  3. 用private修饰的成员,仅能在该类内部使用,在类外部或者派生类中都不能使用。

注意:以上描述中的“使用”,指的是直接引用(如不能在Derived中直接调用privateMethod)。尽管不能直接引用,但是仍然可以通过其他方法来访问(虽然在 Derived中无法直接访问Base的property_,但通过getProperty(),我们仍然可以在需要的时候获取其值)。

除了可以修饰类成员,这三个关键字还可以修饰类的继承关系:

class Base
{
 public:
  void publicBaseMethod()
  {
    cout << __FUNCTION__ << endl;
  }
 protected:
  void protectedBaseMethod()
  {
    cout << __FUNCTION__ << endl;
  }
 private:
  void privateBaseMethod()
  {
    cout << __FUNCTION__ << endl;
  }
};

// public inheritance, all Base's members remain the same scope
class PublicDerived : public Base
{
 public:
  void accessBase()
  {
    // ok
    publicBaseMethod();

    // ok
    protectedBaseMethod();

    // compile error: 'privateBaseMethod' is a private member of 'Base'
    // privateBaseMethod();
  }
};

// protected inheritance, Base's public members will become protected in ProtectedDerived
class ProtectedDerived : protected Base
{
 public:
  void accessBase()
  {
    // ok
    publicBaseMethod();

    // ok
    protectedBaseMethod();

    // compile error: 'privateBaseMethod' is a private member of 'Base'
    // privateBaseMethod();
  }
};

// private inheritance, Base's public and protected members will become private in PrivateDerived
class PrivateDerived : private Base
{
 public:
  void accessBase()
  {
    // ok
    publicBaseMethod();

    // ok
    protectedBaseMethod();

    // compile error: 'privateBaseMethod' is a private member of 'Base'
    // privateBaseMethod();
  }
};

class SonOfPrivateDerived : public PrivateDerived
{
 public:
  void accessParent()
  {
    // compile error: 'publicBaseMethod' is a private member of 'Base'
    // publicBaseMethod();

    // compile error: 'protectedBaseMethod' is a private member of 'Base'
    // protectedBaseMethod();

    // IMPORTANTE NOTE: why PrivateDerived can access publicBaseMethod and protectedBaseMethod,
    // although they are both private members of 'Base'???

    // compile error: 'privateBaseMethod' is a private member of 'Base'
    // privateBaseMethod();
  }
};

int main()
{
  PublicDerived pubd;
  // ok
  pubd.publicBaseMethod();

  // compile error: 'protectedBaseMethod' is a protected member of 'Base'
  // pubd.protectedBaseMethod();

  // compile error: 'privateBaseMethod' is a private member of 'Base'
  // pubd.privateBaseMethod();

  pubd.accessBase();

  ProtectedDerived prod;
  // compile error: 'publicBaseMethod' is a protected member of 'Base',
  // NOTE: the Base's public members now become protected in ProtectedDerived's perspective
  // prod.publicBaseMethod();

  // compile error: 'protectedBaseMethod' is a protected member of 'Base'
  // NOTE: The Base's protected members remain protected in ProtectedDerived's perspective
  // prod.protectedBaseMethod();

  // compile error: 'privateBaseMethod' is a private member of 'Base'
  // NOTE: The Base's private members remain private in ProtectedDerived's perspective
  // prod.privateBaseMethod();

  prod.accessBase();

  PrivateDerived prid;
  // compile error: 'publicBaseMethod' is a private member of 'Base'
  // NOTE: publicBaseMethod now become private in PrivateDerived's perspective
  // prid.publicBaseMethod();

  // compile error: 'protectedBaseMethod' is a private member of 'Base'
  // NOTE: protectedBaseMethod also become private in PrivateDerived's perspective
  // prid.protectedBaseMethod();

  // compile error: 'privateBaseMethod' is a private member of 'Base'
  // prid.privateBaseMethod();

  prid.accessBase();

  SonOfPrivateDerived pubprid;
  // NOTE: check the comments in accessParent to get the point
  pubprid.accessParent();

  return 0;
}

根据以上程序,可以得出public、protected和private在修饰继承关系中的作用为:

  1. 当使用公有继承(public Base)时,派生类中看到的基类成员属性与基类中定义的一致,如示例程序中publicBaseMethod、protectedBaseMethod和privateBaseMethod仍然分别为public、protected和private
  2. 当使用保护继承(protected Base)时,派生类中看到的基类的成员属性与基类定义不一致,原来基类中的public成员在派生类中被认为是基类的protected成员了,如示例中无法通过ProtectedDerived类的实例在类外部调用publicBaseMethod
  3. 当使用私有继承(private Base)时,派生类中看到的基类成员属性同样与基类定义不一致,即原来基类中的public和protected成员在派生类中都被当成了private成员了,如示例中只能在PrivateDerived类内部调用publicBaseMethod和protectedBaseMethod
  4. 在私有继承中,尽管当在外部调用public或protected成员时,其出错信息(通过clang++编译得到的)都是“’xxx’ is a private member of ‘Base’”,事实上并非如此:在私有继承关系中,基类的public和protected成员并非被当做基类的私有成员了,而是被当做派生类的私有成员了。如果是被当做是基类的私有成员,那么PrivateDerived中是无法访问publicBaseMethod和protectedBaseMethod的;但实际上,从PrivateDerived中可以访问publicBaseMethod和protectedBaseMethod,而SonOfPrivateDerived中无法访问它们,因此publicBaseMethod和protectedBaseMethod应该是PrivateDerived的私有成员而非Base的私有成员。

Author: Rex Shen

Created: 2014-07-28 Mon 15:22

Emacs 24.3.1 (Org mode 8.2.7b)

Validate

Leave a comment

Your email address will not be published. Required fields are marked *