构造函数关于初始化列表方面
初始化列表的语法位于构造函数的参数列表和函数体之间,使用 :
来分隔。
class MyClass
{
public:
int x, y;
// 构造函数使用初始化列表初始化 x 和 y
MyClass(int a, int b) : x(a), y(b) {
// 函数体可以进一步处理
}
};
使用初始化列表:
class MyClass {
public:
int x, y;
// 初始化列表初始化成员变量
MyClass(int a, int b) : x(a), y(b) {
// 此时 x 和 y 已经被初始化
}
};
在构造函数体内赋值:
class MyClass {
public:
int x, y;
// 在构造函数体内赋值
MyClass(int a, int b) {
x = a;
y = b;
}
};
虽然看起来这两种方式都能达到相同的效果,但在效率上有所不同。对于内置类型(如 int
、double
),差别不大,但对于复杂类型,初始化列表可以避免不必要的临时对象创建和拷贝操作。
显式在初始化列表中初始化的成员变量:
class MyClass {
public:
int x;
MyClass(int a) : x(a) { // x 显式在初始化列表中被初始化为 a
}
};
未显式在初始化列表中的成员变量:
类中声明位置有缺省值:
class MyClass {
public:
int x = 10; // 类中声明的缺省值
MyClass() { // 构造函数中没有初始化列表,x 将会使用 10 作为初始值
}
};
类中声明位置没有缺省值:
int
、float
等),这些变量的初始值可能是随机的,也可能是 0 或其他默认值,这取决于编译器的行为。class MyClass {
public:
int x; // 没有缺省值,值可能是随机的
MyClass() {
// 没有初始化列表,x 的值是未定义的
}
};
引用成员变量 / const
成员变量 / 没有默认构造函数的成员变量:
const
类型:常量必须在声明时初始化,因为它们的值在整个对象的生命周期内是不可改变的。class MyClass {
public:
const int x; // const 成员
int& y; // 引用成员
MyClass(int a, int& b) : x(a), y(b) { // 必须使用初始化列表
}
};
const
、引用类型成员变量,以及没有默认构造函数的成员变量必须使用初始化列表初始化。在C++中,类型转换(Type Casting)是一种将一个类型的数据转换为另一个类型的机制。这在许多情况下很有用,例如在需要不同精度的算术运算、函数参数传递、类之间的转换等情境下。
int a = 10;
double b = a; // 隐式将 int 类型的 a 转换为 double
在上述例子中,编译器会自动将 int
类型的 a
转换为 double
,以确保类型一致。
C风格的类型转换是最简单的一种方式,但它不推荐在现代 C++ 中使用,因为它不够安全和灵活,无法区分具体的转换种类。
int a = 10;
double b = (double)a; // C 风格的显式类型转换
C++引入了四种更安全的类型转换运算符,用于替代C风格的转换。
1)
static_cast
2)dynamic_cast
3)const_cast
4)reinterpret_cast
• ⽤static修饰的成员变量,称之为静态成员变量,静态成员变量⼀定要在类外进行初始化。 • 静态成员变量为所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区。 • ⽤static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针。 • 静态成员函数中可以访问其他的静态成员,但是不能访问非静态的,因为没有this指针。 • ⾮静态的成员函数,可以访问任意的静态成员变量和静态成员函数。 • 突破类域就可以访问静态成员,可以通过类名::静态成员或者对象.静态成员来访问静态成员变量 和静态成员函数。 • 静态成员也是类的成员,受public、protected、private访问限定符的限制。 • 静态成员变量不能在声明位置给缺省值初始化,因为缺省值是个构造函数初始化列表的,静态成员 变量不属于某个对象,不⾛构造函数初始化列表
static
成员变量 static
成员变量是属于整个类的,而不是某个具体对象的。它在所有该类的对象之间共享,且只在内存中存在一份。static
成员变量在程序开始时就分配内存并初始化,直到程序结束时才销毁。static
成员变量,而不需要实例化对象。#include <iostream>
using namespace std;
class MyClass {
public:
static int count; // 声明静态成员变量
MyClass() {
count++;
}
};
int MyClass::count = 0; // 定义并初始化静态成员变量
int main() {
MyClass obj1;
MyClass obj2;
cout << "Number of objects: " << MyClass::count << endl; // 输出2
return 0;
}
在这个例子中,count
是一个静态成员变量,它在类的所有对象之间共享。创建对象obj1
和obj2
时,count
分别递增。
static
成员函数static
成员函数可以在没有对象的情况下通过类名直接调用。它不能访问非静态成员变量或成员函数,因为它不属于类的任何实例。static
成员函数只能访问static
成员变量或其他static
成员函数。#include <iostream>
using namespace std;
class MyClass {
public:
static int count; // 静态成员变量
static void showCount() { // 静态成员函数
cout << "Count: " << count << endl;
}
};
int MyClass::count = 10;
int main() {
MyClass::showCount(); // 通过类名直接调用静态函数
return 0;
}
在此例子中,showCount
是一个静态成员函数,它访问类的静态成员变量count
,无需对象实例化即可调用。
static
成员变量在所有对象之间共享,且不依赖于任何对象实例。static
成员函数可以通过类名直接调用,且不能访问非静态的成员变量或成员函数。static
成员适合存储和操作类级别的共享信息,比如计数器、全局配置等。
设已经有A,B,C,D4个类的定义,程序中A,B,C,D构造函数调⽤顺序为?() 设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调⽤顺序为?()
C c;
是一个全局对象。A a;
是在 main()
函数中定义的局部对象。B b;
同样是 main()
中的局部对象。static D d;
是 main()
函数中的静态对象。C c
)会在 main()
函数执行之前构造。A a
和 B b
)按照它们在代码中出现的顺序依次构造。static D d
)在它们第一次遇到时进行构造,但它们的生命周期是整个程序的运行期间。main()
函数执行完毕之后)析构。main()
函数执行结束后,所有静态局部对象析构完毕后析构。C
:首先构造,因为它是全局变量,在 main()
函数执行之前构造。A
:在 main()
中第一个被声明,因此紧随其后构造。B
:在 main()
中第二个被声明,接着构造。D
:在 main()
函数中遇到 static D d;
时构造。B
:B
是局部对象,main()
函数结束时,B
最后构造,最先析构。A
:A
是局部对象,在 B
之后析构。D
:D
是静态局部对象,main()
函数结束后析构。C
:C
是全局对象,最后析构。• 友元提供了⼀种突破类访问限定符封装的⽅式,友元分为:友元函数和友元类,在函数声明或者类声明的前⾯加friend,并且把友元声明放到⼀个类的⾥⾯。 • 外部友元函数可访问类的私有和保护成员,友元函数仅仅是⼀种声明,他不是类的成员函数。 • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制。 • ⼀个函数可以是多个类的友元函数。 • 友元类中的成员函数都可以是另⼀个类的友元函数,都可以访问另⼀个类中的私有和保护成员。 • 友元类的关系是单向的,不具有交换性,⽐如A类是B类的友元,但是B类不是A类的友元。 • 友元类关系不能传递,如果A是B的友元,B是C的友元,但是A不是C的友元。 • 有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多⽤。
友元函数是一个不是类成员的函数,但它可以访问该类的所有私有成员和保护成员。要定义友元函数,可以在类定义中使用 friend
关键字声明。
#include <iostream>
using namespace std;
class MyClass {
private:
int x;
public:
MyClass(int val) : x(val) {}
// 声明友元函数
friend void showValue(MyClass& obj);
};
// 定义友元函数
void showValue(MyClass& obj) {
cout << "x = " << obj.x << endl; // 访问私有成员 x
}
int main() {
MyClass obj(10);
showValue(obj); // 调用友元函数
return 0;
}
showValue()
是 MyClass
类的友元函数,它能访问 MyClass
的私有成员 x
。x
是私有成员,但友元函数可以访问它。友元类允许一个类访问另一个类的所有私有和保护成员。在类定义中,可以使用 friend
关键字声明另一个类为其友元类。
#include <iostream>
using namespace std;
class MyClass {
private:
int x;
public:
MyClass(int val) : x(val) {}
// 声明 FriendClass 是 MyClass 的友元类
friend class FriendClass;
};
class FriendClass {
public:
void showValue(MyClass& obj) {
cout << "x = " << obj.x << endl; // 访问私有成员 x
}
};
int main() {
MyClass obj(20);
FriendClass fObj;
fObj.showValue(obj); // 通过友元类访问私有成员
return 0;
}
FriendClass
是 MyClass
的友元类,因此它能够访问 MyClass
中的私有成员 x
。FriendClass::showValue()
访问了 MyClass
的私有成员 x。
你还可以将另一个类的某个成员函数声明为当前类的友元。这允许特定的成员函数访问当前类的私有成员,而不是整个类。
#include <iostream>
using namespace std;
class ClassB; // 前向声明
class ClassA {
private:
int x;
public:
ClassA(int val) : x(val) {}
// 声明 ClassB 的某个成员函数是 ClassA 的友元
friend void ClassB::show(ClassA& obj);
};
class ClassB {
public:
void show(ClassA& obj) {
cout << "x = " << obj.x << endl; // 访问 ClassA 的私有成员 x
}
};
int main() {
ClassA objA(30);
ClassB objB;
objB.show(objA); // 通过 ClassB 的成员函数访问 ClassA 的私有成员
return 0;
}
ClassB
的 show()
函数是 ClassA
的友元成员函数,因此它可以访问 ClassA
的私有成员 x
。ClassB
本身不是 ClassA
的友元类,只有 show()
函数有权访问 ClassA
的私有成员。ClassA
是 ClassB
的友元,ClassB
不会自动成为 ClassA
的友元。类似地,友元关系不会在继承中传递。
• 如果⼀个类定义在另⼀个类的内部,这个内部类就叫做内部类。内部类是⼀个独⽴的类,跟定义在 全局相⽐,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类。 • 内部类默认是外部类的友元类。 • 内部类本质也是⼀种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使⽤,那么可以考 虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的专属内部类,其 他地⽅都⽤不了
求1+2+3+...+n_牛客题霸_牛客网 这题可以让我们更加了解什么是内部类
class Solution {
// 内部类
class Sum
{
public:
Sum()
{
_ret += _i;
++_i;
}
};
static int _i;
static int _ret;
public:
int Sum_Solution(int n)
{
// 变⻓数组
Sum arr[n];
return _ret;
}
};
int Solution::_i = 1;
int Solution::_ret = 0;
匿名对象(Anonymous Object)是指没有名字的对象,即对象在创建时不分配一个显式的变量名。这类对象通常是临时对象,创建后会立即用于某个操作,且在操作完成后就会销毁。匿名对象一般在函数调用、返回值或赋值时出现,并且生命周期非常短。
匿名对象的例子:
1. 函数返回匿名对象:
#include <iostream>
using namespace std;
class MyClass {
public:
MyClass() {
cout << "构造函数被调用" << endl;
}
~MyClass() {
cout << "析构函数被调用" << endl;
}
};
MyClass createObject() {
return MyClass(); // 返回一个匿名对象
}
int main() {
MyClass obj = createObject(); // 调用构造和析构函数
return 0;
}
2. 直接创建匿名对象:
#include <iostream>
using namespace std;
class MyClass {
public:
MyClass() {
cout << "构造函数被调用" << endl;
}
~MyClass() {
cout << "析构函数被调用" << endl;
}
};
int main() {
MyClass(); // 创建了一个匿名对象
cout << "匿名对象创建完毕" << endl;
return 0;
}
3. 作为函数参数的匿名对象:
#include <iostream>
using namespace std;
class MyClass {
public:
MyClass() {
cout << "构造函数被调用" << endl;
}
~MyClass() {
cout << "析构函数被调用" << endl;
}
};
void func(MyClass obj) {
cout << "函数体内" << endl;
}
int main() {
func(MyClass()); // 传递一个匿名对象作为参数
return 0;
}
在C++中,当对象拷贝时,编译器可以进行一些优化来减少不必要的对象构造和销毁操作,尤其是对于临时对象和返回值的拷贝。主要的编译器优化机制包括:
返回值优化(RVO) 是编译器为避免不必要的临时对象拷贝而进行的一种优化技术。当一个函数返回对象时,编译器可以直接在调用代码的目标位置构造返回的对象,而不是创建临时对象再拷贝。这个优化在C++98中就已经存在,并且在C++17中被强制要求成为标准行为,不再需要编译器特定的优化。
命名返回值优化(NRVO) 是RVO的变体,用于处理函数中定义的具名变量的返回优化。编译器可以直接在返回值位置构造这个具名变量,从而避免额外的拷贝和构造操作。
C++11引入了移动语义,包括移动构造函数和移动赋值运算符。移动语义允许编译器将资源从一个临时对象“移动”到目标对象,而不是进行昂贵的拷贝操作。这对于避免不必要的深拷贝非常有用,尤其是在处理动态内存、文件句柄等资源时。
拷贝省略 是C++中最常见的优化之一。即使没有移动语义,编译器也可以直接跳过拷贝操作(拷贝构造函数的调用),并直接将对象构造在目标内存位置。这与RVO和NRVO类似,但它不仅适用于返回值场景,也适用于对象赋值和传递的其他场景。