私有,公有,针对类而言;
私有( private )的成员,自己的,只能在自己内部( 类的定义体内部 )访问,外部( 类的定义体外部 )不能访问/调用;
公有( 或者说公开,public )的成员,对外开放的,可以在自己外面( 类的定义体外部 )被访问到,即,可调用/访问到;
1. 什么是私有继承?其目的与好处是什么?
私有继承是 C++ 中的一种继承方式,它使得基类的公有和保护成员,在派生类中,变为派生类的私有成员。也就是说,派生类的实例对象不能直接访问基类的公有成员,但可以在派生类的定义体内部访问基类的公有成员和保护成员。
"私有继承",类似于一个私生子,私生子可以有父类的能力,但是不能让外人看到其本质,需要私生子自己包装一下,也就是,将父类的接口包含在自己的对外接口的定义体内部。这样,私生子有父类的能力,但是外人也看不到其本质。
目的与好处
-
封装:私有继承可以帮助实现更好的封装。当你不希望,基类的接口暴露给用户时,可以选择私有继承。
-
实现"是一个"的关系 :私有继承可以表示一种"实现"关系,而不是"是一个"关系。这意味着,派生类可以实现基类的功能,但是不能将基类的接口暴露给外部。也就是,私生子可以拥有父类的能力,但是对外不能承认它的身份,不能有父类的标志。所以,是实现 "是一个"的关系。
-
控制访问:通过私有继承,派生类可以控制对基类成员的访问,确保,只有在派生类内部,可以访问基类的公有成员和保护成员。而不能通过派生类的实例对象,访问,基类的公有成员和保护成员!
示例代码
下面是一个示例,演示私有继承的用法:
cpp
#include <iostream>
#include <string>
// 基类 Base
class Base {
public:
void display() const {
std::cout << "Base class display function." << std::endl;
}
protected:
std::string protectedMember = "Protected Member";
};
// 派生类,私有继承 基类 Base
class Derived : private Base {
public:
void show() {
// 可以访问基类的公有和保护成员
display(); // 调用/访问,基类的公有成员 ---> display()方法
std::cout << "Accessing: " << protectedMember << std::endl; // 访问基类的保护成员
}
};
int main() {
Derived derived; // 创建派生类的实例对象
derived.show(); // 访问派生类的公开成员show()
// 下面这行代码会报错,因为 display() 是基类的公有成员,在派生类 Derived 中是私有的
// derived.display(); // 不能在类的外部,访问类的私有成员
return 0;
}
说明
-
基类
Base
:- 包含一个公有成员
display()
和一个保护成员protectedMember
。
- 包含一个公有成员
-
派生类
Derived
:- 私有继承
Base
。这意味着,基类Base
的公有和保护成员,在派生类Derived
中,变为私有成员。 - 在派生类
Derived
的公开成员show()
中,派生类Derived
可以访问基类Base
的 公开成员display()
方法,以及保护成员protectedMember
。
- 私有继承
-
在
main
函数中:- 创建了派生类
Derived
的一个实例对象,并调用派生类的公开成员,show()
方法,成功访问了基类的公开和保护成员。 - 尝试直接调用
derived.display()
,会导致编译错误,因为基类的公开成员display()
,在派生类Derived
中,是私有的,不暴露给外部访问。
- 创建了派生类
运行结果
运行上述代码,输出将是:
Base class display function.
Accessing: Protected Member
这表明,私有继承,成功地封装了基类的接口,只有在派生类Derived
的内部,可以访问基类的公有和保护成员。通过这种方式,可以实现更严格的访问控制和封装。
2. "基类被子类私有继承后,基类的公有成员依然能够被子类访问。"这个对吗?为什么?
对!
基类被派生类私有继承后,基类的公有成员和保护成员,依然能够在派生类的内部被访问。
原因
在 C++ 中,私有继承,意味着,派生类的实例对象不能直接访问基类的公有成员和保护成员!因为,此时,基类的公开成员和保护成员,在派生类中,变成私有的!但是,在派生类的定义体内部,可以访问基类的公有成员和保护成员。这也表明,类的私有成员,只有在类的定义体内部,才可以被访问!
示例代码
下面是一个示例,展示一下,基类的公有成员,如何在私有继承的情况下,被子类访问。
cpp
#include <iostream>
#include <string>
// 基类 Base
class Base {
public:
void show() const {
std::cout << "Base class show function." << std::endl;
}
std::string name = "Base Class";
};
// 派生类Derived,私有继承 基类 Base
class Derived : private Base {
public:
void accessBaseMembers() {
// 可以访问基类的公有成员
show(); // 调用/访问,基类的公有成员 ---> show()方法
std::cout << "Accessing base member: " << name << std::endl; // 访问基类的公有成员变量
}
};
int main() {
Derived derived; // 创建派生类的实例对象
derived.accessBaseMembers(); // 访问派生类的公开成员accessBaseMembers()
// 下面这行代码会报错,因为 show() 是基类的公有成员,但在派生类 Derived 中是私有的
// derived.show(); // 不能在类的外部,访问类的私有成员
return 0;
}
说明
-
基类
Base
:- 定义了一个公有成员函数
show()
和一个公有成员变量name
。
- 定义了一个公有成员函数
-
派生类
Derived
:- 私有继承
Base
。这意味着,Base
的公有成员,在Derived
中变成了Derived
的私有成员。 - 在
accessBaseMembers()
成员函数中,派生类可以访问基类的公有成员函数show()
和公有成员变量name
。
- 私有继承
-
在
main
函数中:- 创建了派生类
Derived
的一个实例对象,并调用派生类Derived
的公有成员accessBaseMembers()
,成功访问了,基类的公有成员。 - 尝试直接调用
derived.show()
,会导致编译错误,因为show()
在 派生类Derived
中,是私有的,不可在派生类Derived
的外部访问。
- 创建了派生类
运行结果
运行上述代码,输出将是:
Base class show function.
Accessing base member: Base Class
这表明,即使基类被私有继承,派生类仍然可以在自己的定义体内部,访问基类的公开成员( 和保护成员 )。
3. 私有继承和公有继承有什么区别?
私有继承和公有继承是 C++ 中两种不同的继承方式,它们在访问控制 、基类与派生类之间的关系,这两点上,有显著区别。以下是它们的主要区别:
(1) 访问控制
-
公有继承:
- 基类的公有成员,在派生类中,仍然是公有的。
- 基类的保护成员,在派生类中,仍然是保护的。
- 基类的私有成员,派生类不可访问。
-
私有继承:
- 基类的公有成员,在派生类中,变为私有的。
- 基类的保护成员,在派生类中,变为私有的。
- 基类的私有成员,派生类不可访问。
(2) 关系的表达
-
公有继承:
- 明确表示,"是一个"(is-a)关系。例如,
Dog
是Animal
的一种。
- 明确表示,"是一个"(is-a)关系。例如,
-
私有继承:
- 明确表示,"实现"(is-implemented-in-terms-of)关系,不承认(隐含) "是一个"关系。通常用于表示,派生类使用基类的功能,但不希望将基类的接口暴露给外部。
(3) 访问方式
-
公有继承:
- 可以通过派生类的实例对象,直接访问,基类的公有成员。
-
私有继承:
- 不能通过派生类的实例对象,直接访问,基类的公有成员;只能在派生类的定义体内部,访问基类的公有成员和保护成员。因此,此时,基类的公开成员和保护成员,在派生类中,变成( 派生类的 )私有的了!
示例代码
以下是一个示例,展示,公有继承和私有继承的区别:
cpp
#include <iostream>
// 基类 Base
class Base {
public:
void publicMethod() {
std::cout << "Base public method." << std::endl;
}
protected:
void protectedMethod() {
std::cout << "Base protected method." << std::endl;
}
};
// 派生类 PublicDevired 公开继承 Base类
class PublicDerived : public Base {
public:
void accessBaseMethods() {
publicMethod(); // 可以访问 基类的公有成员
protectedMethod(); // 可以访问 基类的保护成员
}
};
// 派生类 PrivateDerived 私有继承 Base类
class PrivateDerived : private Base {
public:
void accessBaseMethods() {
publicMethod(); // 可以访问 基类的公有成员
protectedMethod(); // 可以访问 基类的保护成员
}
};
int main() {
PublicDerived pubDerived; // 创建 派生类 PublicDerived的实例对象
pubDerived.accessBaseMethods(); // 访问派生类的公有成员
// 下面这行代码会编译通过
pubDerived.publicMethod(); // 可以直接访问,基类的公有成员
PrivateDerived privDerived; // 创建 派生类 PrivateDerived的实例对象
privDerived.accessBaseMethods(); // 访问派生类的公有成员
// 下面这行代码会报错,因为 publicMethod 在 派生类 PrivateDerived 中是私有的
// privDerived.publicMethod(); // 不能在类的外部,访问类的私有成员
return 0;
}
运行结果
运行上述代码,输出将是:
Base public method.
Base protected method.
Base public method.
Base public method.
Base protected method.
总结
- 公有继承:基类的公有成员,在派生类中,仍然是公有的,允许在派生类的外部访问;表示派生类与基类之间,"是一个"的关系。
- 私有继承:基类的公有成员,在派生类中,变为私有的,在派生类的外部无法访问,只能在派生类内部访问;表示派生类与基类之间,"实现"的关系。
4. 派生类如何访问基类的私有成员?
在 C++ 中,派生类无法直接访问,基类的私有成员。每一个类,其私有成员,只能在自己内部( 自己定义体内部 ),被访问;不能在自己外部( 自己定义体外部 )被访问!
派生类访问基类私有成员的方式
尽管派生类不能直接访问基类的私有成员,但可以通过以下几种方式,间接访问基类的私有成员:
-
通过基类的公开成员方法或保护成员方法:
- 基类可以提供公开成员方法或保护成员方法,在这些方法内部,可以访问基类的私有成员。派生类可以通过调用这些基类的公开成员方法或保护成员方法,来间接访问基类的私有成员。
-
通过基类的友元函数:
- 如果一个友元函数被定义在基类中,它可以访问基类的私有成员。派生类可以通过调用,这个基类的友元函数,来访问基类的私有成员。
示例代码1:通过基类的公开成员方法或保护成员方法
以下是一个示例,展示,派生类如何通过基类的公开成员方法,间接访问基类的私有成员:
cpp
#include <iostream>
// 基类 Base
class Base {
private:
int privateValue = 42; // 基类 Base的 私有成员
protected:
int protectedValue = 24; // 基类 Base的 保护成员
public:
// 基类 Base的公有成员方法,其内部,可以访问基类 Base的私有成员
int getPrivateValue() const {
return privateValue;
}
};
// 派生类 Derived 公开继承 基类 Base
class Derived : public Base {
public:
void showValues() {
// 不能直接访问,基类 Base的私有成员 privateValue
// std::cout << privateValue; // 这一行会报错
// 可以通过,基类 Base的公有成员方法 getPrivateValue(),来访问,基类Base的私有成员
std::cout << "Private Value: " << getPrivateValue() << std::endl;
std::cout << "Protected Value: " << protectedValue << std::endl; // 可以直接访问,基类Base的保护成员
}
};
int main() {
Derived derived; // 创建 派生类 PublicDerived的实例对象
derived.showValues(); // 访问 派生类的公有成员方法
return 0;
}
运行结果
运行上述代码,输出将是:
Private Value: 42
Protected Value: 24
总结
- 直接访问:派生类不能直接访问基类的私有成员。
- 间接访问 :
- 通过基类的公有成员方法或保护成员方法,来访问,基类的私有成员。
- 通过基类的友元函数,来访问,基类的私有成员。
这种设计,确保了,数据的封装性和安全性,只有通过特定的接口,才能访问类的私有数据。
示例代码2:通过基类的友元函数
下面是一个详细的示例,展示,派生类如何通过基类的友元函数,来间接访问/调用,基类的私有成员。
cpp
#include <iostream>
// 基类Base
class Base {
private:
int privateValue = 42; // 基类Base的 私有成员
public:
// 声明 基类Base的友元函数 accessPrivateValue( 在类的内部声明 )
friend void accessPrivateValue(const Base& b);
};
// 基类Base的友元函数 accessPrivateValue的定义( 在类的外部定义 )
void accessPrivateValue(const Base& b) {
// 在友元函数内部,可以访问,基类Base的私有成员
std::cout << "Accessing Base's private value: " << b.privateValue << std::endl;
}
// 派生类Derived 公开继承 基类Base
class Derived : public Base {
public:
// 派生类Derived的 公有成员方法
void showPrivateValue() {
// 通过基类Base的友元函数,来访问基类Base的私有成员
accessPrivateValue(*this); // 传递,当前派生类Derived的实例对象(哪个Derived的实例对象,调用showPrivateValue()方法,传递的就是,该Derived实例对象)
}
};
int main() {
Derived derived;
derived.showPrivateValue(); // 调用派生类的公有成员方法
return 0;
}
代码解析
-
基类
Base
:- 定义了一个私有成员方法
privateValue
。 - 声明了一个友元函数
accessPrivateValue
;基类Base
的这个友元函数,可以访问基类Base
的私有成员。
- 定义了一个私有成员方法
-
基类
Base
的 友元函数accessPrivateValue
:- 声明在基类
Base
的内部,定义在基类Base
的外部。由于它是 基类Base
的友元函数,因此可以访问 基类Base
的私有成员privateValue
。
- 声明在基类
-
派生类
Derived
:- 继承基类
Base
。 - 定义了一个公有成员方法
showPrivateValue
,这个方法内部,调用了基类Base
的友元函数accessPrivateValue
;基类Base
的友元函数accessPrivateValue
,将,当前派生类Derived
的实例对象(*this
)作为参数,进行传递。( 哪个Derived的实例对象,调用showPrivateValue()方法,传递的就是,该Derived实例对象 )
- 继承基类
-
main
函数:- 创建了一个
Derived
类的实例对象derived
。 - 调用
derived.showPrivateValue()
,这将输出,基类Base
的私有成员的值。
- 创建了一个
运行结果
运行上述代码,输出将是:
Accessing Base's private value: 42
总结
- 友元函数 :在基类
Base
的内部,将accessPrivateValue
函数声明为 基类Base
的友元函数,该函数便获得了,访问 基类Base
的私有成员privateValue
的权限。( 声明在 基类Base
的内部,定义在 基类Base
的外部 ) - 派生类访问 :派生类
Derived
通过调用,基类Base
的友元函数accessPrivateValue
,间接访问了,基类Base
的私有成员privateValue
。
这种设计使得,基类的私有数据受到保护,同时提供一种安全的方式,让派生类通过友元函数,访问基类的私有数据。
5. 在使用基类的友元函数时,派生类公有继承基类时,与私有继承基类时,会有什么区别?
在 C++ 中,基类的友元函数的使用,与派生类继承基类的方式( 公有继承、私有继承等 )有关,主要体现在 访问权限 、友元关系 上。
以下是,派生类公有继承基类、派生类私有继承基类,在使用基类的友元函数时的一些区别:
-
公有继承:
- 基类的友元函数内部,可以访问,基类的所有公有成员、所有保护成员、所有私有成员;
- 公有继承时,基类的公有成员和保护成员,在派生类中,也是公有的;在派生类的外部,可以调用基类的友元函数,传递派生类的实例对象,来访问基类的所有公有成员、所有保护成员、所有私有成员;
-
私有继承:
- 基类的友元函数内部,可以访问,基类的所有公有成员、所有保护成员、所有私有成员;
- 私有继承时,基类的公有成员和保护成员,在派生类中,是私有的;在派生类的外部,不能调用基类的友元函数,来传递派生类的实例对象,以访问基类的公有成员和私有成员。因为,此时,基类的公有成员和保护成员,在派生类中,是私有的,不能在类的外部,访问类的私有成员!
- 基类的友元函数,只能在派生类的内部被调用,传递派生类的实例对象,来访问基类的所有公有成员、所有保护成员、所有隐私成员;
派生类对基类的私有继承,或者公开继承,不会影响基类的友元函数内部,对基类的所有成员的访问!有影响的是,公有继承的情况下,可以 在派生类的外部,直接调用基类的友元函数,传递派生类的实例对象!而私有继承的情况下,不可以 在派生类的外部,直接调用基类的友元函数,传递派生类的实例对象;只能在派生类的内部,直接调用基类的友元函数,传递派生类的实例对象!
示例代码1:公有继承和私有继承在友元函数使用上的区别
下面是一个示例代码,展示公有继承和私有继承在友元函数使用上的区别:
cpp
#include <iostream>
// 基类 Base
class Base {
public:
int publicValue = 10;
protected:
int protectedValue = 20;
// 声明 友元函数 friendFunction
friend void friendFunction(Base& b);
};
// 派生类 PublicDerived 公开继承 基类 Base
class PublicDerived : public Base {
public:
void show() {
// 访问基类 Base的公开成员 publicVaule
std::cout << "PublicDerived accessing Base: " << publicValue << std::endl;
}
};
// 派生类 PrivateDerived 私有继承 基类 Base
class PrivateDerived : private Base {
public:
void show() {
// 访问基类 Base的公开成员 publicVaule
std::cout << "PrivateDerived accessing Base: " << publicValue << std::endl;
}
};
// 基类 Base的友元函数 的定义
void friendFunction(Base& b) {
std::cout << "Friend function accessing Base: " << b.publicValue << std::endl;
std::cout << "Friend function accessing Base: " << b.protectedValue << std::endl;
}
int main() {
Base base; // 创建一个基类 Base的实例对象
friendFunction(base); // 友元函数 可以直接传递 基类 Base的实例对象,访问 基类 Base 的所有成员
PublicDerived pubDerived; // 创建派生类 PublicDerived的实例对象
pubDerived.show(); // 访问派生类 PublicDerived的公有成员
PrivateDerived privDerived; // 创建派生类 PrivateDerived的实例对象
privDerived.show(); // 访问派生类 PrivateDerived的公有成员
// 在派生类 PublicDerived的外部,直接调用基类 Base的友元函数,传递派生类 PublicDerived的实例对象
friendFunction(pubDerived);
// friendFunction(privDerived); // 这行会报错,派生类 PrivateDerived 私有继承 基类 Base!所以,不能在派生类 PrivateDerived的外部,直接调用基类 Base的友元函数,传递派生类 PublicDerived的实例对象!
return 0;
}
运行结果
运行上述代码,输出将是:
Friend function accessing Base: 10
Friend function accessing Base: 20
PublicDerived accessing Base: 10
PrivateDerived accessing Base: 10
Friend function accessing Base: 10
Friend function accessing Base: 20
在 C++ 中,当使用私有继承时,只能在派生类的成员函数中,调用基类 的友元函数,传递 派生类的实例对象!
下面是一个详细的示例代码来说明这一点。
示例代码2:私有继承时,只能在派生类的成员函数中,调用基类 的友元函数,传递 派生类的实例对象!
cpp
#include <iostream>
// 基类 Base
class Base {
public:
void show() {
std::cout << "Base class show function." << std::endl;
}
// 声明 基类 Base的友元函数
friend void friendFunction(Base& b);
};
// 派生类 Derived 私有继承 基类 Base
class Derived : private Base {
public:
void display() {
std::cout << "Derived class display function." << std::endl;
// 在派生类的成员函数中可以访问基类的公有成员
show(); // 访问基类的公有成员
}
// 提供一个对外的公开接口函数,在内部,调用基类 Base的友元函数
void callFriendFunction() {
friendFunction(*this); // 调用基类 Base的友元函数,传递派生类的实例对象
}
};
// 定义基类 Base的友元函数
void friendFunction(Base& b) {
b.show(); // 在基类 Base的友元函数内部,可以访问 基类 Base 的所有成员
}
int main() {
Base b; // 创建基类 Base的实例对象
friendFunction(b); // 直接在基类 Base的外部,调用其友元函数,传递基类 Base的实例对象
Derived d; // 创建派生类 Derived的实例对象
// friendFunction(d); // 这行会报错!派生类 Derived 私有继承 基类 Base!所以,不能在派生类 Derived的外部,直接调用基类 Base的友元函数,传递派生类 Derived的实例对象!
d.display(); // 调用派生类 Derived的公开成员函数
d.callFriendFunction(); // 通过派生类Derived的对外公开成员函数,间接调用基类 Base的友元函数
return 0;
}
代码解析
-
基类
Base
:- 定义了一个公有成员函数
show()
,输出一条信息。 - 声明了一个友元函数
friendFunction()
,允许其访问Base
的所有成员。
- 定义了一个公有成员函数
-
派生类
Derived
:- 私有继承
private Base
,这意味着,Base
的公有成员和保护成员在Derived
中都会变为私有的。 - 定义了一个公开成员函数
display()
,可以访问基类的公有成员show()
。 - 定义了一个公开成员函数
callFriendFunction()
,用于间接调用基类 Base的友元函数friendFunction()
。
- 私有继承
-
友元函数
friendFunction()
:- 接受一个
Base
类型的引用( 可以是基类 Base的实例对象,可以是基类 Base的子类的实例对象 ),可以访问Base
的公有成员show()
。
- 接受一个
-
main()
函数:- 创建了一个基类
Base
的实例对象b
,在基类Base
的外部,直接调用其友元函数friendFunction()
,传递基类Base
的实例对象; - 创建了一个派生类
Derived
的实例对象d
,在派生类Derived
的外部,直接调用基类Base
的友元函数friendFunction()
, 会导致错误,因为派生类Derived
是私有继承基类Base
,所以,不能在派生类Derived
的外部,直接调用 基类Base
的友元函数! - 调用派生类
Derived
的公开成员函数display()
,可以调用 基类Base
的友元函数friendFunction()
。
- 创建了一个基类
总结
-
公有继承:
- 可以在派生类的外部,直接调用基类的友元函数,传递派生类的实例对象;
-
私有继承:
- 无法在派生类的外部,直接调用基类的友元函数,传递派生类的实例对象!但是,可以在派生类的对外公开成员函数内部,调用基类的友元函数!从而,实现,间接调用基类的友元函数!
这种区别,使得,在设计类的接口和访问控制时,选择合适的继承方式和友元函数的使用,变得尤为重要。