和小男生做的网站,代理公司注册步骤,微商分销如何搭建分销模式,怎么做家具定制网站⭐在对C 中类的6个默认成员函数有了初步了解之后#xff0c;现在我们进行对类相关特性的深入探讨#xff01; #x1f525;#x1f525;#x1f525;【C】类的默认成员函数#xff1a;深入剖析与应用#xff08;上#xff09; 【C】类的默认成员函数#xff1a;深入剖…⭐在对C 中类的6个默认成员函数有了初步了解之后现在我们进行对类相关特性的深入探讨 【C】类的默认成员函数深入剖析与应用上 【C】类的默认成员函数深入剖析与应用下 目录
前言
再谈构造函数
一构造函数体赋值
二初始化列表
三explicit关键字
static成员
一静态成员变量
二静态成员函数
友元
一友元函数
二友元类
内部类
一定义与访问
二内部类的用途
总结 前言 在 C 编程中类是构建复杂程序的基石。它提供了一种将数据和操作数据的方法进行封装的机制使得程序更加模块化、可维护和可扩展。在之前对类的学习中我们已经了解了一些基本概念如构造函数、拷贝构造函数和析构函数等。然而类还有许多其他重要的特性这些特性对于深入理解和掌握 C 编程至关重要。本文将进一步探讨构造函数的更多细节以及 Static 成员、友元、内部类等特性并再次深入理解封装的概念。 再谈构造函数 构造函数在 C 类中扮演着至关重要的角色。它主要用于对象的初始化操作。当创建一个类的对象时构造函数会被自动调用。 一构造函数体赋值
在创建对象时编译器通过调用构造函数给对象中各个成员变量一个合适的初始值。
class Date {
public:Date(int year, int month, int day) {_year year;_month month;_day day;}private:int _year;int _month;int _day;
};
❓问题如下 虽然在构造函数中通过赋值操作给对象的成员变量赋予了初始值但严格意义上来说这只是在构造函数体中进行的赋初值操作而不是真正的初始化。 真正的初始化是在对象创建时就确定下来并且只能进行一次。而在构造函数体内部可以进行多次赋值操作这就与初始化的概念有所不同。 现在我们引出一个概念来解决这一问题——初始化列表 二初始化列表 为了实现真正的初始化可以使用初始化列表。 初始化列表是在构造函数的参数列表之后函数体之前以冒号开头后面跟着一系列成员变量的初始化表达式。 代码如下
class Date {
public:Date(int year, int month, int day) : _year(year), _month(month), _day(day) {// 构造函数体可以进行其他操作但这里不能再对成员变量进行初始化}
private:int _year;int _month;int _day;
}; ❗注意 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)类中包含以下成员必须放在初始化列表位置进行初始化引用成员变量const成员变量自定义类型成员(且该类没有默认构造函数时) #include iostream// 自定义类 CustomClass 的定义
class CustomClass {
public:// 构造函数接收一个整数值进行初始化CustomClass(int val) : value(val) {std::cout CustomClass constructor called with value: value std::endl;}
private:int value;
};// 主类 MyClass 的定义
class MyClass {
public:int ref; // 引用成员变量const int constVal; // const 成员变量CustomClass custom; // 自定义类型成员变量// 构造函数接收一个引用、一个整数和一个整数作为参数MyClass(int r, int v, int customVal) : ref(r), constVal(v), custom(customVal) {std::cout MyClass constructor called. std::endl;}
};int main() {int num 10;// 创建 MyClass 对象传入相应参数MyClass obj(num, 20, 30);return 0;
} ☝MyClass包含了引用成员变量、const成员变量和一个自定义类型的成员变量。在构造函数中必须使用初始化列表来正确初始化这些特殊类型的成员变量。 尽量使用初始化列表初始化因为不管你是否使用初始化列表✅对于自定义类型成员变量一定会先使用初始化列表初始化。 #include iostreamclass CustomType {
public:CustomType(int val) : data(val) {std::cout CustomType constructor called with value: data std::endl;}
private:int data;
};class MyClass {
public:MyClass(int customVal) : customMember(customVal) {std::cout MyClass constructor called. std::endl;}
private:CustomType customMember;
};int main() {MyClass obj(42);return 0;
} 在这个例子中MyClass有一个自定义类型CustomType的成员变量customMember。当创建MyClass的对象时即使在MyClass的构造函数中没有显式地写出初始化列表但实际上编译器会先尝试使用初始化列表来初始化customMember这就调用了CustomType的构造函数并输出相应信息。如果CustomType没有默认构造函数那么就必须在MyClass的构造函数中显式地使用初始化列表来正确初始化customMember。 成员变量在类中声明次序就是其在初始化列表中的初始化顺序与其在初始化列表中的先后次序无关 #include iostreamclass MyClass {
public:MyClass(int a, int b, int c) : c_member(c), b_member(b), a_member(a) {std::cout Constructor called. std::endl;}void printMembers() {std::cout a_member: a_member std::endl;std::cout b_member: b_member std::endl;std::cout c_member: c_member std::endl;}private:int a_member;int b_member;int c_member;
};int main() {MyClass obj(1, 2, 3);obj.printMembers();return 0;
} 在这个例子中构造函数的初始化列表中成员变量的初始化顺序看起来是 c_member、b_member、a_member但实际上成员变量的初始化顺序是由它们在类中的声明顺序决定的即先初始化 a_member再初始化 b_member最后初始化 c_member。如果在初始化列表中打乱顺序初始化的结果仍然是按照声明顺序进行的。 三explicit关键字
构造函数不仅可以构造与初始化对象对于接收单个参数的构造函数还具有类型转换的作用。
接收单个参数的构造函数具体表现 构造函数只有一个参数构造函数有多个参数除第一个参数没有默认值外其余参数都有默认值全缺省构造函数 #include iostreamclass Date
{
public:// 1. 单参构造函数没有使用 explicit 修饰具有类型转换作用// explicit 修饰构造函数禁止类型转换---explicit 去掉之后代码可以通过编译explicit Date(int year): _year(year){}/*// 2. 虽然有多个参数但是创建对象时后两个参数可以不传递没有使用 explicit 修饰具有类型转换作用// explicit 修饰构造函数禁止类型转换explicit Date(int year, int month 1, int day 1): _year(year), _month(month), _day(day){}*/Date operator(const Date d){if (this! d){_year d._year;_month d._month;_day d._day;}return *this;}private:int _year;int _month;int _day;
};void Test()
{Date d1(2022);// 用一个整形变量给日期类型对象赋值// 实际编译器背后会用 2023 构造一个无名对象最后用无名对象给 d1 对象进行赋值// 将 1 屏蔽掉2 放开时则编译失败因为 explicit 修饰构造函数禁止了单参构造函数类型转换的作用
} 上述代码可读性不是很好用explicit修饰构造函数将会禁止构造函数的隐式转换。 有一个接受单个整数参数的构造函数Date(int year)用于初始化日期对象的年份部分。这个构造函数使用了explicit关键字修饰这意味着它不能进行隐式类型转换。如果没有这个关键字编译器可能会在某些情况下自动使用这个构造函数进行隐式的类型转换。 static成员 一静态成员变量 静态成员变量是属于类的变量而不是属于某个具体的对象。它在整个类的所有对象之间共享。 1.定义与初始化
静态成员变量需要在类内声明但不能在类内初始化除了一些特殊的静态常量整数类型可以在类内初始化。例如 class MyClass {
public:static int staticVar;
};
int MyClass::staticVar 0; // 在类外初始化 2.访问方式
可以通过类名直接访问静态成员变量也可以通过对象访问但通常建议通过类名访问以体现其类属性。例如 MyClass obj;
MyClass::staticVar 10; // 通过类名访问
obj.staticVar 20; // 通过对象访问也是允许的但不规范
二静态成员函数 静态成员函数也是属于类的函数它不依赖于具体的对象实例。 1.特点
它只能访问静态成员变量和其他静态成员函数因为它没有this指针指向具体的对象。例如 class MyClass {
public:static int staticVar;static void staticFunction() {staticVar; // 可以访问静态成员变量// 不能访问非静态成员变量因为没有this指针}
};
2.调用方式
与静态成员变量类似可以通过类名直接调用静态成员函数。例如MyClass::staticFunction(); 友元 ⭐友元机制允许一个类或函数访问另一个类的私有成员。它打破了类的封装性但在某些特定情况下是非常有用的。 一友元函数 1.定义
友元函数是在一个类中声明为友元的普通函数。它不是类的成员函数但可以访问该类的私有成员。例如 class MyClass {
private:int privateVar;
public:friend void friendFunction(MyClass obj);
};
void friendFunction(MyClass obj) {// 可以访问obj的privateVarstdcout obj.privateVar std::endl;
}
❗说明 友元函数可访问类的私有和保护成员但不是类的成员函数友元函数不能用const修饰友元函数可以在类定义的任何地方声明不受类访问限定符限制一个函数可以是多个类的友元函数友元函数的调用与普通函数的调用原理相同 二友元类 1.定义
友元类是在一个类中声明为友元的另一个类。友元类的所有成员函数都可以访问声明它为友元的类的私有成员。例如 class MyClass {
private:int privateVar;
public:friend class FriendClass;
};
class FriendClass {
public:void accessPrivate(MyClass obj) {// 可以访问obj的privateVarstdcout obj.privateVar std::endl;}
};
2.使用场景
当两个类之间存在紧密的合作关系需要相互访问对方的私有成员时可以使用友元类。例如一个图形绘制类和一个图形变换类可能需要相互访问对方的私有数据来实现复杂的图形操作。 内部类 内部类是定义在另一个类内部的类。它具有一些特殊的性质和用途。 一定义与访问 1.定义
内部类可以在一个外部类的任何部分定义包括私有部分、保护部分和公共部分。例如 class OuterClass {
private:int outerVar;class InnerClass {public:void innerFunction() {// 可以访问OuterClass的成员吗这取决于具体情况}};
};
2.访问
外部类可以通过创建内部类的对象来访问内部类的成员。内部类也可以访问外部类的成员但需要注意访问权限。如果内部类定义在外部类的公共部分那么它可以像普通类一样被外部访问和使用。如果定义在私有部分只有外部类的成员函数可以创建内部类的对象并访问其的成员。例如 OuterClass outerObj;
OuterClass::InnerClass innerObj; // 创建内部类对象如果InnerClass是公共的
outerObj.innerObj.innerFunction(); // 通过外部区
outerObj.innerObj.innerFunction(); // 通过外部类对象访问内部类对象的成员如果InnerClass是公共的且有合适的访问路径
二内部类的用途 1.隐藏实现细节
内部类可以将一些与外部类相关但又不想暴露给外部的实现细节封装起来。例如一个复杂的容器类可能使用内部类来实现其内部的数据结构如链表节点类可以作为容器类的内部类。
2.实现辅助功能
可以利用内部类来实现一些辅助功能这些功能与外部类紧密相关但又不适合作为外部类的直接成员函数。例如一个文件读取类可能有一个内部类用于处理文件的缓冲和读取位置等细节。 总结 C 中类的这些特性为我们提供了强大的编程工具让我们能够更好地组织和管理代码。希望本文能够帮助你更深入地理解 C 类的相关特性提升你的编程能力。 以后我将深入研究继承、多态、模板等特性并将默认成员函数与这些特性结合以解决更复杂编程问题欢迎关注我【A Charmer】