C++

一.类型转换
在C++中,类类型转换是指将一个类的对象转换为另一个类的对象,或者将一个类的对象转换为基本数据类型等。C++提供了多种类型的转换机制,包括隐式类型转换和显式类型转换。以下是关于C++类类型转换的详细介绍:
隐式类型转换
构造函数实现的隐式转换
- 如果一个类的构造函数只有一个参数(除了可能的默认参数外),那么这个构造函数可以被用作隐式类型转换。例如:
class B { public: B(int x) { /* ... */ } };
这时,可以将
int
类型的值隐式转换为B
类型的对象:B b = 5; // 通过B的构造函数将5隐式转换为B对象
在类类型有多个参数时,可以使用{ xxx ,xxx },的形式来进行转换,不能使用(),因为()会计算出括号内最后一个表达式的值
类型转换运算符实现的隐式转换
- 类可以定义类型转换运算符,将类的对象隐式转换为其他类型。例如:
class A { public: operator int() const { return 10; } };
这时,
A
类型的对象可以隐式转换为int
类型:A a; int x = a; // 通过A的类型转换运算符将a隐式转换为int
这种隐式转换可能会导致一些意外的错误,因此可以通过在构造函数前加上
explicit
关键字来禁止这种隐式转换:class B { public: explicit B(int x) { /* ... */ } };
这样,
B b = 5;
就会报错,而必须显式地调用构造函数:B b(5);
。类类型的对象之间也可以隐式转换,需要相应的构造函数⽀持
二.static成员
在C++中,static
成员是类的一个重要特性,它与类本身相关联,而不是与类的某个具体对象相关联。这意味着static
成员在类的所有对象之间是共享的。以下是对static
成员的详细介绍:
一、static
成员变量
- 定义与初始化
static
成员变量是类的所有对象共享的变量。它在类的定义中声明,但需要在类的定义外进行初始化。 ⽤static修饰的成员变量,称之为静态成员变量,静态成员变量⼀定要在类外进⾏初始化。例如:
class MyClass { public: static int count; // 声明static成员变量 }; int MyClass::count = 0; // 初始化static成员变量
- 注意,
static
成员变量的初始化必须在类的定义外进行,且只能初始化一次。
- 访问方式
static
成员变量可以通过类名或类的对象来访问,但推荐使用类名访问,以体现其与类的关系。例如:
MyClass::count = 10; // 通过类名访问 MyClass obj; obj.count = 20; // 通过对象访问(不推荐,但合法)
- 无论通过类名还是对象访问,
static
成员变量都是同一个变量。
- 生命周期
static
成员变量的生命周期从程序开始运行时初始化,到程序结束时销毁。它不依赖于类的对象的生命周期。- 静态成员变量不能在声明位置给缺省值初始化,因为缺省值是个构造函数初始化列表的,静态成员变量不属于某个对象,不⾛构造函数初始化列表.
二、static
成员函数
- 定义与调用
static
成员函数是与类相关联的函数,而不是与类的某个具体对象相关联。它不能访问非static
成员变量和非static
成员函数,因为这些成员需要对象的上下文。例如:
class MyClass { public: static void print() { std::cout << "Static function" << std::endl; } };
static
成员函数可以通过类名或类的对象来调用,但推荐使用类名调用。例如:
MyClass::print(); // 通过类名调用 MyClass obj; obj.print(); // 通过对象调用(不推荐,但合法)
- 特点
static
成员函数没有this
指针,因为它不依赖于具体的对象。- 它可以访问类的
static
成员变量和其他static
成员函数。 - 静态成员也是类的成员,受public、protected、private访问限定符的限制。
- 应用场景
- 常用于实现与类相关的工具函数,这些函数不需要访问类的对象的成员变量。例如,实现一个全局的计数器:
class MyClass { public: static void incrementCount() { count++; } static int getCount() { return count; } private: static int count; }; int MyClass::count = 0; int main() { MyClass::incrementCount(); std::cout << MyClass::getCount() << std::endl; // 输出1 return 0; }
三、static
成员的存储位置
static
成员变量和static
成员函数的存储位置通常在程序的全局数据区。它们在程序启动时分配内存,在程序结束时释放内存。- 与全局变量类似,
static
成员变量在全局数据区中有一个固定的地址,所有对象共享这个地址。 - 静态成员变量为所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区。
四、总结
static
成员变量和static
成员函数是类的静态特性,它们与类本身相关联,而不是与类的对象相关联。static
成员变量需要在类的定义外初始化,并且可以通过类名或对象访问。⽤static修饰的成员函数,称之为静态成员函数
static
成员函数没有this
指针,只能访问类的static
成员变量和其他static
成员函数。- ⾮静态的成员函数,可以访问任意的静态成员变量和静态成员函数。
- 静态成员函数中可以访问其他的静态成员,但是不能访问⾮静态的,因为没有this指针。
- 突破类域就可以访问静态成员,可以通过类名::静态成员或者对象.静态成员 来访问静态成员变量和静态成员函数。
三.友元
在C++中,友元(Friend) 是一个非常重要的概念,它允许某些特定的函数或类访问另一个类的私有(private
)和保护(protected
)成员。这在某些情况下非常有用,尤其是当你需要在类的封装性与功能需求之间找到平衡时。
一、友元函数
- 定义
- 友元函数不是类的成员函数,但它可以访问类的私有和保护成员。友元函数需要在类的定义中使用
friend
关键字声明。
- 友元函数不是类的成员函数,但它可以访问类的私有和保护成员。友元函数需要在类的定义中使用
- 特点
- 友元函数不是类的成员函数,因此它没有
this
指针。 - 友元函数的定义可以位于类的定义内或定义外,但声明必须在类的定义中。
- 友元函数可以访问类的所有成员(包括私有和保护成员)。
- ⼀个函数可以是多个类的友元函数。
- 友元函数不是类的成员函数,因此它没有
二、友元类
- 定义
- 友元类是一个类,它的所有成员函数都可以访问另一个类的私有和保护成员。友元类需要在类的定义中使用
friend
关键字声明。例如:
class MyClass { private: int privateData; public: MyClass(int data) : privateData(data) {} friend class FriendClass; // 声明友元类 }; class FriendClass { public: void printPrivateData(const MyClass& obj) { std::cout << "Private Data: " << obj.privateData << std::endl; } };
- 在这个例子中,
FriendClass
是MyClass
的友元类,因此FriendClass
的所有成员函数都可以访问MyClass
的私有成员privateData
。
- 友元类是一个类,它的所有成员函数都可以访问另一个类的私有和保护成员。友元类需要在类的定义中使用
- 特点
- 友元类的所有成员函数都可以访问被友元类声明的类的私有和保护成员。
- 友元类的声明方式与友元函数类似,但声明的是整个类。
三、友元成员函数
- 定义
- 友元成员函数是另一个类的成员函数,但它可以访问当前类的私有和保护成员。友元成员函数需要在类的定义中使用
friend
关键字声明,并指定所属类和函数名。
- 友元成员函数是另一个类的成员函数,但它可以访问当前类的私有和保护成员。友元成员函数需要在类的定义中使用
- 特点
- 友元成员函数是另一个类的成员函数,但它可以访问当前类的私有和保护成员。
- 友元成员函数的声明需要指定所属类和函数名。
- 元类中的成员函数都可以是另⼀个类的友元函数,都可以访问另⼀个类中的私有和保护成员。
- 友元类的关系是单向的,不具有交换性,⽐如A类是B类的友元,但是B类不是A类的友元。
- 友元类关系不能传递,如果A是B的友元,B是C的友元,但是A不是C的友元。
四、总结
- 友元函数:允许特定的非成员函数访问类的私有和保护成员。
- 友元类:允许另一个类的所有成员函数访问当前类的私有和保护成员。
- 友元成员函数:允许另一个类的特定成员函数访问当前类的私有和保护成员。
- 使用注意事项:谨慎使用友元,尽量减少对类的内部结构的暴露,避免破坏封装性。友元破坏了类的封装性,因为它允许外部函数或类访问类的私有和保护成员。因此,应该谨慎使用友元,只在必要时才使用。
四.内部类
• 如果⼀个类定义在另⼀个类的内部,这个内部类就叫做内部类。内部类是⼀个独⽴的类,跟定义在全局相⽐,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类。
• 内部类默认是外部类的友元类。
• 内部类本质也是⼀种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使⽤,那么可以考虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的专属内部类,其他地⽅都⽤不了。
内部类和外部类为平行关系,计算大小时不算内部类,内部类不算外部类的一部分,收访问限定符的限制
五.匿名对象
通常情况下,我们定义的是有名对象。在C++中,匿名对象是指没有显式命名的对象。它们通常在需要临时使用某个类的对象时被创建,使用完毕后立即销毁。匿名对象⽣命周期只在当前⼀⾏,⼀般临时定义⼀个对象当前⽤⼀下即可。
六.new
当使用动态内存分配(new)创建数组时,构造函数的调用机制与静态数组是相同的,但有一些额外的注意事项。
动态数组的构造函数调用
Sum* arr = new Sum[n]; // 动态创建长度为n的Sum数组
这行代码会:
- 在堆上分配足够存储n个Sum对象的内存
- 逐个调用每个元素的默认构造函数
- 返回指向第一个元素的指针
- 与静态数组一样,构造函数会被调用n次