再谈构造函数
在创建对象时,编译器可以通过调用构造函数给成员变量赋值,但这并不是初始化,因为初始化只能初始化一次,但是赋值显然可以有多次赋值。
这里就引入初始化列表概念:
初始化列表是C++中用来初始化类成员变量的一种语法。它可以在构造函数的初始化列表中指定初始化值,而不是在构造函数的函数体中进行初始化。这种方式可以提高效率并减少代码量。
例如,对于以下类:
class MyClass {
public:
MyClass(int x, int y)
:a(x),
b(y)
{}
private:
int a;
int b;
};
构造函数的初始化列表为“: a(x), b(y)” 。这表明在创建MyClass对象时,a的初始值为x,b的初始值为y。如果没有使用初始化列表,构造函数将会像这样:
class MyClass {
public:
MyClass(int x, int y)
{
a = x;
b = y;
}
private:
int a;
int b;
};
这种构造函数的效率不如使用初始化列表的高,因为它需要在构造函数的函数体中执行两个赋值操作。在实际应用中,当需要初始化大量成员变量时,使用初始化列表可以明显提高程序的性能。
注意:
1、每个成员变量在初始化列表只能出现一次,也就是只能被初始化一次
2、引用成员变量、const成员变量、自定义类型成员变量(且类没有默认构造函数)必须要在初始化列表初始化。
3、成员变量的声明顺序就是初始化列表的初始顺序,与其在初始化列表中的顺序无关
explicit关键字
在C++中,explicit
是一个关键字,用于修饰单参数构造函数。它的作用是防止隐式类型转换,即只能通过显式调用构造函数来进行对象的构造,而不能通过隐式转换来构造对象。
举一个例子,考虑下面这个类:
class MyClass {
public:
MyClass(int x) : a(x) {}
private:
int a;
};
如果我们定义一个函数,需要传入一个MyClass
对象作为参数,那么这个函数可以这样声明:
void func(MyClass obj);
如果我们传入一个int
类型的参数给这个函数,那么编译器会自动进行类型转换,将这个int
类型的参数转换为一个MyClass
对象。
func(5); // 会自动转换为 func(MyClass(5))
但是,如果我们在MyClass
的构造函数前加上explicit
关键字,就能禁止隐式类型转换。那么上述代码就会编译错误,必须要显式地创建一个MyClass
对象才能传入函数:
class MyClass {
public:
explicit MyClass(int x) : a(x) {}
private:
int a;
};
func(MyClass(5)); // 正确的写法
func(5); // 编译错误,不能隐式转换
使用explicit
关键字可以避免一些不必要的隐式类型转换,使代码更加明确和健壮。但需要注意的是,如果在实际应用中过度使用explicit
可能会导致代码变得冗长和难懂。
static成员
概念:声明为static的类成员,称为静态成员,用static修饰的函数称为静态成员函数。静态成员变量一定要在外面进行初始化。
class test
{
private://定义静态成员变量
static int _a;
};
//在类外部进行赋值
int test::_a = 0;
静态成员变量与函数的特性:
1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制
class MyClass {
public:
static int count;
};
int MyClass::count = 0; // 定义静态数据成员
int main() {
MyClass obj1, obj2;
MyClass::count++; // 通过类名访问静态数据成员
obj1.count++; // 通过对象名访问静态数据成员
obj2.count++;
cout << "Count: " << MyClass::count << endl; // 输出3
}
静态成员函数是指在类中声明的静态函数,它不属于任何对象,而属于整个类。静态成员函数可以使用类的静态数据成员,但不能使用类的非静态数据成员和成员函数。静态成员函数可以通过类名访问,也可以通过对象名访问。例如:
class MyClass {
public:
static void foo() {
cout << "This is a static member function." << endl;
}
};
int main() {
MyClass::foo(); // 通过类名调用静态成员函数
MyClass obj;
obj.foo(); // 通过对象名调用静态成员函数
}
静态成员可以在类中被用来表示某个类相关的信息或者状态,常用于记录对象的数目、实现单例模式等。使用静态成员需要注意生命周期和作用域问题,以及与非静态成员的区别。
友元
友元是C++中的一个特殊机制,它可以让某个函数或类访问一个类的私有成员。可以说友元是一种授权机制,它允许外部程序或者其他类访问当前类的私有成员。
友元可以是一个函数、一个类、或者一个函数模板。友元声明通常出现在类定义中,并在类定义体外进行定义。友元有两种类型:普通友元和类友元。
普通友元可以是一个全局函数或者一个类的成员函数,可以访问当前类的所有成员,包括私有成员。例如:
class A {
public:
int a;
friend void foo(A obj) {
cout << "a = " << obj.a << endl;
}
};
int main() {
A obj;
obj.a = 10;
foo(obj); // 调用友元函数
return 0;
}
类友元可以让另一个类访问当前类的私有成员,即两个类之间建立友好关系。类友元通常在另一个类的定义中进行声明,并在类定义体外进行定义。例如:
class B;
class A {
private:
int a;
public:
friend class B;
};
class B {
public:
void foo(A obj) {
cout << "a = " << obj.a << endl;
}
};
int main() {
A obj;
B b1;
b1.foo(obj); // 调用类友元函数
return 0;
}
需要注意的是,友元机制会降低封装性,不当使用会导致安全问题。因此,应该慎重使用友元机制,只在必要的情况下使用。
内部类
概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。
注意:内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。
特性:
1. 内部类可以定义在外部类的public、protected、private都是可以的。
2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象 / 类名。
3. sizeof(外部类) = 外部类,和内部类没有任何关系。
class A
{
private:
static int k;
int h;
public:
class B // B天生就是A的友元
{
public:
void foo(const A& a)
{
cout << k << endl;//OK
cout << a.h << endl;//OK
}
};
};
int A::k = 1;
int main()
{
A::B b;
b.foo(A());
return 0;
}