网站建设全包方案,做网站常见的语言,西安旅游攻略,本地wordpress外网访问文章目录
前言 #x1f4ac; 欢迎讨论#xff1a;如果你在学习过程中有任何问题或想法#xff0c;欢迎在评论区留言#xff0c;我们一起交流学习。你的支持是我继续创作的动力#xff01; #x1f44d; 点赞、收藏与分享#xff1a;觉得这篇文章对你有帮助#xff01…文章目录
前言 欢迎讨论如果你在学习过程中有任何问题或想法欢迎在评论区留言我们一起交流学习。你的支持是我继续创作的动力 点赞、收藏与分享觉得这篇文章对你有帮助别忘了点赞、收藏并分享给更多的小伙伴哦你们的支持是我不断进步的动力 分享给更多人如果你觉得这篇文章对你有帮助欢迎分享给更多对C感兴趣的朋友让我们一起进步 4. 友元 在C中友元friend提供了一种突破类的访问限定符的机制使得外部函数或其他类可以访问类的私有private和受保护的成员protected。友元可以是友元函数或友元类而这种友元关系是在类定义中通过关键字 friend 显式声明的。 4.1 友元涉及的基本概念 一 友元函数友元函数可以访问类的私有和受保护成员但它并不是类的成员函数。 二友元类某个类的所有成员函数都可以是另一个类的友元允许访问该类的私有和受保护成员。 三单向关系友元关系是单向的如果A类是B类的友元B类的成员函数可以访问A类的私有成员但A类不能访问B类的私有成员除非B类也显式声明A类为友元。 四友元的局限性虽然友元提供了便利但它打破了类的封装性增加了类之间的耦合因此不宜滥用。
4.2 友元函数 友元函数是一个外部函数但通过友元声明它可以访问类的私有和受保护的成员。友元函数不属于类的成员函数它可以在类的任意地方声明而不受访问限定符public、private、protected的限制。 示例友元函数访问两个类的私有成员
#includeiostream
using namespace std;// 前置声明避免类A的友元函数不识别类B
class B;class A {// 友元声明允许函数 func 访问A类的私有成员friend void func(const A aa, const B bb);private:int _a1 1;int _a2 2;
};class B {// 友元声明允许函数 func 访问B类的私有成员friend void func(const A aa, const B bb);private:int _b1 3;int _b2 4;
};// 友元函数定义能够访问A和B类的私有成员
void func(const A aa, const B bb) {cout A::_a1: aa._a1 endl; // 访问A类的私有成员cout B::_b1: bb._b1 endl; // 访问B类的私有成员
}int main() {A aa;B bb;func(aa, bb); // 调用友元函数访问A和B类的私有成员return 0;
}解释
函数 func 被声明为 A 和 B 类的友元因此它可以访问 A 类和 B 类的私有成员变量 _a1 和 _b1。虽然 func 是一个独立于类的外部函数但通过友元声明它获得了访问类的私有数据的权限。
4.3 友元类 友元类允许一个类的所有成员函数访问另一个类的私有和受保护成员。友元类的成员函数并不需要逐一声明为友元只要类被声明为友元所有的成员函数都能访问另一个类的私有和受保护成员。 示例友元类的使用
#includeiostream
using namespace std;class A {// 友元类B声明允许B类的所有成员函数访问A类的私有成员friend class B;private:int _a1 1;int _a2 2;
};class B {
public:// 可以访问A类的私有成员void func1(const A aa) {cout A::_a1: aa._a1 endl; // 访问A类的私有成员cout B::_b1: _b1 endl; // 访问B类的私有成员}void func2(const A aa) {cout A::_a2: aa._a2 endl; // 访问A类的私有成员cout B::_b2: _b2 endl; // 访问B类的私有成员}private:int _b1 3;int _b2 4;
};int main() {A aa;B bb;bb.func1(aa); // 通过B类的成员函数访问A类的私有成员bb.func2(aa); // 通过B类的成员函数访问A类的私有成员return 0;
}解释
B 类被声明为 A 类的友元类因此 B 类的所有成员函数都可以访问 A 类的私有成员 _a1 和 _a2。通过友元类声明不需要逐个将 B 类的成员函数声明为 A 类的友元只要 B 类是 A 类的友元B 类的所有成员函数都可以访问 A 类的私有数据。
4.4 友元的特性与限制 单向关系友元关系是单向的如果 A 是 B 的友元那么 B 类的成员可以访问 A 类的私有成员但 A 类不能访问 B 类的私有成员除非 B 类也将 A 类声明为友元。 示例单向友元关系
class A;class B {friend class A; // B 声明 A 为友元
private:int _b1 1;
};class A {
public:void accessB(B bb) {// A 可以访问 B 的私有成员cout B::_b1: bb._b1 endl;}
};int main() {A aa;B bb;aa.accessB(bb); // A 类访问 B 的私有成员return 0;
}不具有传递性友元关系不具有传递性。如果 A 是 B 的友元B 是 C 的友元A 不能访问 C 类的私有成员。 友元增加耦合性虽然友元机制提供了访问类私有成员的便利但过度使用友元会导致类与类之间的耦合增加破坏了类的封装性。因此友元不宜滥用应该谨慎使用。
4.5 友元函数与类的实际应用
友元在某些情况下能提供方便比如当需要两个类之间进行紧密合作时使用友元可以简化代码减少冗长的接口设计。
#includeiostream
using namespace std;class Account;class Transaction {
public:void deposit(Account account, double amount);void withdraw(Account account, double amount);
};class Account {friend class Transaction; // 声明 Transaction 类为友元类
public:Account(double balance) : _balance(balance) {}void showBalance() const {cout Balance: _balance endl;}private:double _balance;
};void Transaction::deposit(Account account, double amount) {account._balance amount; // 直接访问 Account 类的私有成员
}void Transaction::withdraw(Account account, double amount) {if (amount account._balance) {account._balance - amount;} else {cout Insufficient balance endl;}
}int main() {Account myAccount(1000.0);Transaction trans;trans.deposit(myAccount, 500.0); // 存款myAccount.showBalance(); // 输出1500trans.withdraw(myAccount, 200.0); // 取款myAccount.showBalance(); // 输出1300return 0;
}解释
Transaction 类被声明为 Account 类的友元类因此 Transaction 类的成员函数 deposit 和 withdraw 可以直接访问 Account 类的私有成员 _balance。
这种情况下友元机制简化了类与类之间的合作不必通过公共接口访问私有数据减少了不必要的代码冗。
总结 友元机制在C中提供了一种打破类封装的方式允许外部函数或类访问类的私有和受保护成员。它通过friend关键字来声明友元函数或友元类使得类之间的合作更加简便。 友元函数和友元类都有其特定的用途友元函数可以访问多个类的私有成员而友元类则使得另一个类的所有成员函数都可以访问当前类的私有数据。 友元关系是单向的不具有传递性过度使用友元会破坏类的封装性和增加类的耦合性应该谨慎使用。
5. 内部类 内部类Nested Class是指一个类定义在另一个类的内部。在C中内部类和外部类是独立的类尽管它们之间有一定的联系但内部类不属于外部类的对象它有自己的内存布局和独立性。使用内部类通常是为了封装和简化类之间的关联。 5.1 内部类的基本概念 1.独立性尽管内部类是定义在外部类的内部但它是一个独立的类。外部类的对象并不包含内部类的对象。也就是说创建外部类的对象时并不会自动创建内部类的对象内部类需要单独实例化。 2.友元关系内部类默认是外部类的友元类这意味着内部类可以访问外部类的私有成员。 3.封装使用内部类可以将一些只在外部类内部使用的逻辑封装起来使代码更加紧凑和可控。内部类可以定义在 private 或 protected 访问限定符下限制其他类对其的访问。
5.2 内部类的使用示例
#includeiostream
using namespace std;class A {
private:static int _k; // 外部类的静态成员int _h 1; // 外部类的非静态成员public:// 定义内部类 Bclass B {public:// 内部类方法可以访问外部类的私有成员因为 B 是 A 的友元类void foo(const A a) {cout A::_k _k endl; // 访问外部类的静态成员cout A::_h a._h endl; // 访问外部类的非静态成员}};
};// 初始化外部类的静态成员
int A::_k 1;int main() {cout Size of A: sizeof(A) endl; // 输出 A 类的大小A::B b; // 创建内部类 B 的对象A aa; // 创建外部类 A 的对象b.foo(aa); // 使用内部类对象调用其方法访问外部类的私有成员return 0;
}解释
内部类 B 被定义在外部类 A 的 public 区域中但它依然是 A 的友元类可以访问 A 类的私有成员变量 _k 和 _h。 创建了 A::B b 来实例化内部类 B然后通过内部类的成员函数 foo 访问外部类对象的私有成员。 sizeof(A) 表示 A 类的大小由于 A 只有一个整数成员 _h因此其大小为4字节。
5.3 封装与访问权限 内部类作为外部类的一部分可以被放置在 private 或 protected 访问区域中这样可以控制内部类的可见性。 示例将内部类放在 private 区域
#includeiostream
using namespace std;class Outer {
private:class Inner { // 内部类定义在 private 区域public:void display() {cout Inner class method called. endl;}};public:void createInner() {Inner in; // 外部类的方法中可以创建内部类的对象in.display();}
};int main() {Outer outer;outer.createInner(); // 通过外部类的方法调用内部类的方法// Outer::Inner in; // 错误内部类在 private 区域外部无法访问return 0;
}解释
在这个例子中内部类 Inner 定义在 Outer 类的 private 区域外部类的方法 createInner() 可以创建 Inner 类的对象并调用其方法。尝试在外部直接访问 Inner 类会导致编译错误因为它是 private 的。
5.4 内部类的封装与应用场景 使用内部类的一个常见场景是当两个类紧密相关时可以将一个类封装到另一个类中。这样做的目的是让外部类管理内部类的访问使得内部类只为外部类所用。 场景内部类作为外部类的专属工具类
#includeiostream
using namespace std;class Manager {
private:class Task {public:void performTask() {cout Performing task. endl;}};public:void assignTask() {Task t; // 外部类方法可以使用内部类t.performTask();}
};int main() {Manager mgr;mgr.assignTask(); // 调用外部类的方法执行内部类中的任务逻辑return 0;
}解释
这里Task 类被封装在 Manager 类的 private 区域表示 Task 只为 Manager 类服务外部无法直接访问它。 这是一种封装技术用于使 Task 类专属于 Manager 类外部无法创建 Task 对象只能通过 Manager 类的方法来间接使用它。 5.5 内部类的友元关系 内部类默认是外部类的友元类这意味着内部类可以访问外部类的私有和受保护成员。这种设计允许内部类和外部类之间进行紧密的合作使得内部类可以像外部类的成员函数一样访问其内部数据。 示例内部类访问外部类的私有成员
#includeiostream
using namespace std;class Container {
private:int _data 100;public:// 定义内部类class Helper {public:void showData(const Container c) {cout Container::_data c._data endl; // 访问外部类的私有成员}};
};int main() {Container c;Container::Helper h; // 创建内部类对象h.showData(c); // 调用内部类的方法访问外部类的私有成员return 0;
}解释
Helper 类作为 Container 的内部类默认是 Container 的友元因此它可以访问 Container 类的私有成员 _data。通过内部类的对象 h可以调用 showData 方法来访问外部类 Container 的私有数据。
5.6 应用求 1 2 3 … n
#includeiostream
using namespace std;class Solution {// 内部类 Sum用于进行累加操作class Sum {public:Sum() {_ret _i; // 每创建一个对象累加一次当前的 _i_i; // 自增 i}};static int _i; // 用于计数的静态变量static int _ret; // 用于存储结果的静态变量public:int Sum_Solution(int n) {Sum arr[n]; // 创建 n 个 Sum 对象触发累加逻辑return _ret; // 返回累加的结果}
};// 初始化静态变量
int Solution::_i 1;
int Solution::_ret 0;int main() {Solution sol;cout Sum of 1 to 5: sol.Sum_Solution(5) endl; // 1 2 3 4 5 15return 0;
}解释
内部类 Sum 在创建对象时会自动进行累加操作创建 n 个 Sum 对象等价于对 1 到 n 进行
累加。
静态变量 _i 用于记录当前的计数_ret 用于存储累加的结果。
总结 内部类是一种封装机制允许将类定义在另一个类的内部从而限制内部类的可见性或封装内部逻辑。内部类与外部类独立但它默认可以访问外部类的私有成员。 内部类的主要优势是封装性和紧密耦合。当一个类主要是为了另一个类服务时将其设计为内部类可以减少外部依赖和接口冗余。 内部类可以用于实现复杂的逻辑封装、类间的紧密合作、计算封装等多个场景但应谨慎使用避免过度增加类的复杂性。 6. 匿名对象 匿名对象是C中的一种特殊对象和普通的有名对象不同匿名对象没有名字仅在表达式中被使用生命周期非常短暂。它的生命周期只限于当前语句当语句执行结束后匿名对象就会自动被销毁并调用析构函数。匿名对象的典型用法是临时定义对象完成某项任务后立即销毁。 6.1 匿名对象的基本概念 匿名对象的定义匿名对象是通过直接调用构造函数创建的对象而没有为其指定名字。形式上它看起来像 A() 或 A(1) 这样的表达式。
生命周期匿名对象的生命周期非常短暂只有在当前表达式结束时存在表达式执行完毕后匿名对象立即调用析构函数被销毁。
应用场景匿名对象通常用于临时性操作例如快速调用某个对象的成员函数或操作符而不需要将该对象保存在变量中。
匿名对象 vs 有名对象 有名对象对象名(实参)
例B pro(10);有名对象 生命周期与作用域相关当作用域结束时对象销毁。 匿名对象类型(实参)
例B(5);匿名对象 生命周期只在当前表达式有效随后立即销毁。
6.2 匿名对象的创建与销毁
在C中通过 B() 或 B(10) 这样的语法直接调用构造函数来创建匿名对象匿名对象没有名字生命周期仅限于当前行结束后立即调用析构函数进行销毁.
示例
#includeiostream
using namespace std;class A {
public:// 构造函数A(int a 0) : _a(a) {cout A(int a) 构造函数被调用, _a _a endl;}// 析构函数~A() {cout ~A() 析构函数被调用, _a _a endl;}private:int _a;
};int main() {A aa1; // 有名对象 aa1 的创建// 不能这样定义对象因为编译器无法确定是函数声明还是对象定义// A aa1();// 创建匿名对象并立即销毁A(); A(1); A aa2(2); // 有名对象 aa2 的创建生命周期为整个作用域// 匿名对象用于调用函数完成任务后立即销毁Solution().Sum_Solution(10);return 0;
}输出 A(int a) 构造函数被调用, _a 0 ~A() 析构函数被调用, _a 0 A(int a) 构造函数被调用, _a 1 ~A() 析构函数被调用, _a 1 A(int a) 构造函数被调用, _a 2 ~A() 析构函数被调用, _a 2 解释
A() 和 A(1) 创建的是匿名对象它们在当前语句结束后立即调用析构函数。有名对象 aa1 和 aa2 是在整个作用域内存在的它们在作用域结束时调用析构函数。匿名对象 的使用场景之一是调用某个方法或操作符后立即销毁不占用额外的资源。
6.3 匿名对象的应用场景
6.3.1 匿名对象用于临时调用成员函数 匿名对象的一个常见应用场景是用来临时调用某个类的成员函数执行完任务后不需要该对象的存在。 class Solution {
public:int Sum_Solution(int n) {return n * (n 1) / 2;}
};int main() {// 使用匿名对象调用 Sum_Solution 函数int result Solution().Sum_Solution(10); // 匿名对象创建后立即销毁cout Sum of 1 to 10: result endl;return 0;
}解释
匿名对象 Solution() 被创建用于调用 Sum_Solution 函数。函数调用结束后匿名对象立即销毁不再占用资源。这是一种常见的设计模式适用于不需要保存对象状态的场景。
6.3.2 匿名对象避免对象命名
示例返回匿名对象
class A {
public:A(int a) : _a(a) {cout A(int a) 构造函数被调用, _a _a endl;}~A() {cout ~A() 析构函数被调用, _a _a endl;}private:int _a;
};// 函数返回一个匿名对象
A createA() {return A(100); // 返回匿名对象
}int main() {createA(); // 调用 createA 函数返回的匿名对象立即销毁return 0;
}输出 A(int a) 构造函数被调用, _a 100 ~A() 析构函数被调用, _a 100 解释
函数 createA 返回一个匿名对象返回后立即销毁。匿名对象在不需要进一步使用的情况下能够有效减少对象创建和销毁的负担。
6.4 匿名对象的注意事项 生命周期短暂匿名对象的生命周期只在当前语句结束时有效不能跨语句使用匿名对象。如果需要在多行代码中使用对象必须创建有名对象。 错误示例 A obj A(1); // 正确有名对象 obj A(1).foo(); // 匿名对象调用方法 // A(1); // 错误匿名对象无法在下一行使用 2.编译器解析问题在C中有些语法可能导致编译器误判为函数声明而不是对象创建。因此注意避免如下情况
错误示例 A aa1(); // 被误判为函数声明实际上不是对象的创建 正确用法
A aa1(1); // 明确创建对象 3.匿名对象的返回值优化RVO现代C编译器通常会对匿名对象进行优化在返回对象时避免多余的拷贝操作。这种优化称为返回值优化RVO。
总结
匿名对象是没有名字的临时对象生命周期非常短暂通常用于一次性操作如临时调用成员函数或返回值。匿名对象在表达式结束后立即调用析构函数销毁适用于不需要持久化对象的场景。匿名对象避免了额外的命名和管理开销在简化代码的同时提高了代码的简洁性和可读性。 相信通过这篇文章你对C类与对象高级部分的有了初步的了解。如果此篇文章对你学习C有帮助期待你的三连你的支持就是我创作的动力
下一篇文章再会.