前言
一切从语法开始
一 C++语法
1.1 c++中Public/Private/Protected变量的继承
public、private、protected是C++中的访问修饰符,用来指定类成员的访问权限。
public:成员在任何地方都是可访问的。private:成员只能被类的成员函数、友元函数或类的实例访问。protected:成员只能被类的成员函数、友元函数、派生类的成员函数或派生类的实例访#include <iostream> using namespace std; // 基类 class Base { public: int publicData; // 公有成员 protected: int protectedData; // 保护成员 private: int privateData; // 私有成员 }; // 派生类 class Derived : public Base { public: void accessMembers() { publicData = 1; // 直接访问公有成员 protectedData = 2; // 直接访问保护成员 // privateData = 3; // 错误!无法直接访问私有成员 } }; int main() { Derived d; d.publicData = 5; // 正确:公有成员在类外可访问 // d.protectedData = 6; // 错误:保护成员在类外不可访问 // d.privateData = 7; // 错误:私有成员在类外不可访问 return 0; }在这个例子中,
Derived类继承自Base类。Derived类的成员函数accessMembers可以访问Base类的公有成员和保护成员,但不能直接访问私有成员。在main函数中,我们只能访问Derived对象的公有成员,不能访问保护成员和私有成员。
1.2 c++中Public/Private/Protected成员函数的继承
成员函数的继承访问是非常灵活和复杂的,请看下面的示例:
#include <iostream>
using namespace std;
// 基类
class Base {
public:
void publicFunc() {
cout << "Public function in Base." << endl;
}
protected:
void protectedFunc() {
cout << "Protected function in Base." << endl;
}
private:
void privateFunc() {
cout << "Private function in Base." << endl;
}
};
// 派生类,使用public继承
class DerivedPublic : public Base {
public:
void accessMembers() {
publicFunc(); // 可以访问公有成员函数
protectedFunc(); // 可以访问保护成员函数
// privateFunc(); // 错误!无法访问私有成员函数
}
};
// 派生类,使用protected继承
class DerivedProtected : protected Base {
public:
void accessMembers() {
publicFunc(); // 可以访问公有成员函数,但在DerivedProtected外部将不可访问
protectedFunc(); // 可以访问保护成员函数
// privateFunc(); // 错误!无法访问私有成员函数
}
};
// 派生类,使用private继承
class DerivedPrivate : private Base {
public:
void accessMembers() {
publicFunc(); // 可以访问公有成员函数,但在DerivedPrivate外部将不可访问
protectedFunc(); // 可以访问保护成员函数
// privateFunc(); // 错误!无法访问私有成员函数
}
};
int main() {
DerivedPublic dp;
dp.publicFunc(); // 正确:公有成员函数在类外可访问
// dp.protectedFunc(); // 错误:保护成员函数在类外不可访问
// dp.privateFunc(); // 错误:私有成员函数在类外不可访问
DerivedProtected dp2;
// dp2.publicFunc(); // 错误:公有成员函数在protected继承下,类外不可访问
// dp2.protectedFunc(); // 错误:保护成员函数在类外不可访问
DerivedPrivate dp3;
// dp3.publicFunc(); // 错误:公有成员函数在private继承下,类外不可访问
// dp3.protectedFunc(); // 错误:保护成员函数在类外不可访问
return 0;
}
在这个例子中,基类Base有三个成员函数:publicFunc、protectedFunc和privateFunc,它们分别是公有、保护和私有的。
请仔细阅读以下说明:
然后,我们定义了三个派生类DerivedPublic、DerivedProtected和DerivedPrivate,它们分别使用public、protected和private继承Base类。
在DerivedPublic中,所有从Base继承来的公有和保护成员函数都可以在派生类的成员函数内部访问,但私有成员函数无法访问。在类外部,只有公有成员函数可以访问。
在DerivedProtected中,所有从Base继承来的公有和保护成员函数都可以在派生类的成员函数内部访问,但私有成员函数无法访问。在类外部,所有继承来的成员函数都不可访问。
在DerivedPrivate中,情况与DerivedProtected相同,但在类外部,所有继承来的成员函数都不可访问,即使它们在基类中是公有的。
1.3 c++中Public/Private/Protected成员函数和成员变量的继承中权限管理
在C++中,使用public、private、protected继承时,成员函数和成员变量的访问权限管理在本质上是相似的,但有一些细微的差别需要注意。
-
继承类型对成员访问权限的影响:
- public继承:基类的公有成员和保护成员按照原有的访问权限在派生类中保持不变,而基类的私有成员在派生类中不可访问。
- private继承:基类的公有成员和保护成员在派生类中变为私有成员,而基类的私有成员在派生类中仍然不可访问。
- protected继承:基类的公有成员和保护成员在派生类中变为保护成员,而基类的私有成员在派生类中仍然不可访问。
-
成员函数和成员变量的特殊之处:
-
成员函数:无论继承类型如何,派生类的成员函数总是可以访问基类中的公有成员和保护成员(除非这些成员在基类中被声明为不可访问,例如通过私有继承)。私有成员在派生类中始终不可访问。
-
成员变量:成员变量的访问权限受继承类型的影响,与成员函数相同。但是,当通过派生类的对象访问成员变量时,访问权限还受到成员变量在派生类中的声明的影响。
-
-
访问权限的“叠加”效应:
- 当派生类继承自基类时,派生类的成员函数可以访问基类中的公有成员和保护成员(根据继承类型可能有所变化)。但是,当通过派生类的对象访问这些成员时,实际的访问权限是基类中的访问权限和派生类中可能的任何额外声明(如使用
using声明)的结合。
- 当派生类继承自基类时,派生类的成员函数可以访问基类中的公有成员和保护成员(根据继承类型可能有所变化)。但是,当通过派生类的对象访问这些成员时,实际的访问权限是基类中的访问权限和派生类中可能的任何额外声明(如使用
-
using声明的作用:- 在派生类中,可以使用
using声明来改变基类成员的访问权限。例如,如果基类有一个私有成员,派生类无法通过using声明来直接访问它,但如果基类有一个保护成员,派生类可以使用using声明将其在派生类中的访问权限提升为公有。
- 在派生类中,可以使用
综上所述,虽然成员函数和成员变量在继承时的访问权限管理在本质上是相似的,但成员函数总是可以访问基类中的公有成员和保护成员(除非这些成员在基类中被声明为不可访问),而成员变量的访问权限还受到通过派生类对象访问时的影响。此外,using声明可以在派生类中用来改变基类成员的访问权限(但有一些限制)。
1.4 C++中private成员的访问
在C++中,将类的成员声明为private意味着这些成员只能被该类的成员函数、友元函数或静态函数访问,而不能被类的外部直接访问,也不能被派生类继承或访问,下面举出一个例子来说明以上特征
#include <iostream>
class MyClass {
private:
int myPrivateVar; // 私有成员变量
public:
MyClass(int value) : myPrivateVar(value) {} // 构造函数
void setMyPrivateVar(int value) {
myPrivateVar = value; // 成员函数可以访问私有成员
}
int getMyPrivateVar() const {
return myPrivateVar; // 成员函数可以访问私有成员,并将其值返回
}
friend void friendFunction(MyClass& obj); // 友元函数声明
};
// 友元函数实现,可以访问类的私有成员
void friendFunction(MyClass& obj) {
std::cout << "Friend function accessing private var: " << obj.myPrivateVar << std::endl;
}
// 尝试从类的外部访问私有成员(这将导致编译错误)
// void externalFunction(MyClass& obj) {
// std::cout << obj.myPrivateVar << std::endl; // 编译错误:'int MyClass::myPrivateVar' is private
// }
int main() {
MyClass obj(10);
std::cout << "Initial value: " << obj.getMyPrivateVar() << std::endl;
obj.setMyPrivateVar(20);
std::cout << "Updated value: " << obj.getMyPrivateVar() << std::endl;
friendFunction(obj); // 友元函数访问私有成员
// externalFunction(obj); // 这行如果取消注释,将导致编译错误
return 0;
}
在这个例子中,MyClass有一个私有成员变量myPrivateVar。这个变量只能在MyClass的成员函数(如setMyPrivateVar和getMyPrivateVar)和友元函数(如friendFunction)中被访问。如果你尝试在类的外部直接访问myPrivateVar,比如取消注释externalFunction函数中的代码,编译器将报错,因为myPrivateVar是私有的,不能被类的外部访问。
此外,由于myPrivateVar是私有的,它也不会被派生类继承。如果你尝试创建一个从MyClass派生的类,并试图访问myPrivateVar,你也会遇到编译错误。
1.5 C++中protected成员的访问
在C++中,protected关键字用于声明类的成员,这些成员可以被该类自身的成员函数、友元函数以及派生类中的成员函数访问,但不能被类的外部直接访问。这是protected与private的主要区别:protected成员在派生类中是可访问的,而private成员则不是。
举例说明:
#include <iostream>
class Base {
protected:
int protectedVar;
public:
Base(int value) : protectedVar(value) {}
void setProtectedVar(int value) {
protectedVar = value;
}
int getProtectedVar() const {
return protectedVar;
}
};
class Derived : public Base {
public:
Derived(int value) : Base(value) {}
void increment() {
// 派生类可以访问基类的protected成员
protectedVar++;
}
void display() const {
// 派生类可以访问基类的protected成员
std::cout << "Protected var: " << protectedVar << std::endl;
}
};
// 尝试从类的外部访问protected成员(这将导致编译错误)
// void externalFunction(Base& obj) {
// std::cout << obj.protectedVar << std::endl; // 编译错误:'int Base::protectedVar' is protected
// }
int main() {
Derived obj(10);
obj.increment();
obj.display(); // 输出:Protected var: 11
// externalFunction(obj); // 如果取消注释,将导致编译错误
return 0;
}
Base类有一个protected成员变量protectedVar。这个变量可以在Base类的成员函数中被访问,也可以在从Base类派生的Derived类的成员函数中被访问。但是,如果你尝试在类的外部直接访问protectedVar,比如取消注释externalFunction函数中的代码,编译器将报错,因为protectedVar是受保护的,不能被类的外部访问。
1.6 几个有趣的小问题
1.6.1 C++中" :: "与" : "的作用
"::":这个符号被称为“作用域解析运算符”。
它的作用是在特定的作用域内访问变量、函数或类型。比如,如果你有一个类名为MyClass,并且这个类里有一个静态成员变量staticVar,那么你可以通过MyClass::staticVar来访问这个静态成员变量。这个符号告诉编译器:“我要访问的是MyClass作用域内的staticVar”。
":":这个符号在C++中主要有两个用途。
- 第一个用途是在类的继承中,用来表示一个类是从另一个类继承而来的。比如,class Derived : public Base表示Derived类是从Base类继承而来的。
- 第二个用途是在构造函数的初始化列表中,用来初始化类的成员变量。比如,如果你有一个类MyClass,它有一个成员变量intVar,那么你可以在构造函数中使用: intVar(value)来初始化intVar,其中value是一个传递给构造函数的参数。
1.6.2 静态成员与动态(非静态)成员
在C++中,类的成员可以分为静态成员和动态成员(尽管“动态成员”这个词不是C++的官方术语,但这里我们可以将其理解为非静态成员,因为静态成员的对立面就是非静态成员,也就是与具体对象实例相关联的成员)。我会尽量用通俗易懂的方式来解释它们。
静态成员
静态成员是属于类的,而不是属于类的某个具体对象的。这意味着,无论你创建了多少个类的对象,静态成员都只有一份拷贝。静态成员可以是静态变量或静态函数。
-
静态变量:它们在类的所有对象之间共享。例如,如果你有一个表示银行账户的类,并且你想要追踪创建了多少个账户,你可以使用一个静态变量来实现这一点。
-
静态函数:它们可以在不创建类对象的情况下被调用。静态函数只能访问静态成员变量和调用其他的静态成员函数。
动态成员(非静态成员)
动态成员(或非静态成员)是属于类的具体对象的。每个对象都有自己的动态成员拷贝。动态成员可以是成员变量或成员函数。
-
成员变量:它们定义了对象的属性。例如,在银行账户类中,账户余额和账户号码可能是成员变量。
-
成员函数:它们是可以在对象上执行的操作。例如,在银行账户类中,存款和取款可能是成员函数。
举例说明
假设你有一个BankAccount类,它有一个静态变量totalAccounts来追踪创建的账户数量,以及一个非静态变量balance来表示每个账户的余额。
class BankAccount {
public:
static int totalAccounts; // 静态变量
double balance; // 非静态变量
BankAccount() {
totalAccounts++; // 每次创建新对象时增加账户数量
balance = 0.0; // 初始化余额
}
static int getTotalAccounts() { // 静态函数
return totalAccounts;
}
void deposit(double amount) { // 非静态函数
balance += amount;
}
void withdraw(double amount) { // 非静态函数
if (amount <= balance) {
balance -= amount;
}
}
};
// 初始化静态变量
int BankAccount::totalAccounts = 0;
在这个例子中,totalAccounts是一个静态变量,它属于BankAccount类,而不是类的任何具体对象。无论你创建了多少个BankAccount对象,totalAccounts都只有一份拷贝,并且它可以通过类的静态函数getTotalAccounts来访问。 相反,balance是一个非静态变量,它属于BankAccount类的每个具体对象。每个BankAccount对象都有自己的balance拷贝,并且它可以通过对象的成员函数(如deposit和withdraw)来访问和修改。
1.6.3 构造函数与初始化列表
1.6.4 命名空间
命名空间(Namespace)在C++中并不是类,它是一种将库名称封装起来的方法,以避免名称冲突。理解命名空间的一种方式是将其视为一个容器,它包含了一系列的标识符(比如变量名、函数名等),这些标识符可以被组织在一起以避免命名冲突。
使用方法:
namespace 命名空间名 {
// 在这里定义变量、函数、类等
}
例如,定义一个名为MyNamespace的命名空间,并在其中定义一个函数myFunction和一个变量myVariable:
namespace MyNamespace {
int myVariable = 0;
void myFunction() {
// 函数实现
}
}
使用命名空间中的元素时,需要使用命名空间名::元素名的格式,例如:
MyNamespace::myFunction();
int x = MyNamespace::myVariable;
使用using声明或using指令来避免在每次访问命名空间成员时都重复命名空间名:
using namespace MyNamespace;
// 现在也可以直接使用myFunction和myVariable而不需要前缀
myFunction();
int x = myVariable;
5714

被折叠的 条评论
为什么被折叠?



