C++: 类和对象(下)
1.0 类型转换
- C++⽀持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数、
- 构造函数前⾯加explicit就不再⽀持隐式类型转换
class QQ {
public:
//加上explicit就不支持隐私类型转换
//explicit QQ(int q1)
QQ(int q1)
{
qq1 = q1;
cout << "QQ constructed with int: " << q1 << endl;
}
//explicit QQ(int q1, int q2)
QQ(int q1, int q2)
:qq1(q1)
,qq2(q2)
{}
void show() const {
cout << "Value: " << qq1 << qq2 << endl;
}
private:
int qq1;
int qq2;
};
int main() {
//构造⼀个QQ的临时对象,再⽤这个临时对象拷⻉构造a3
// 编译器遇到连续构造+拷⻉构造->优化为直接构造
QQ a = 1;
a.show();
const QQ& a2 = 1;
//C++11之后才⽀持多参数转化
QQ a3 = { 3,3 };
return 0;
}
2.0 static成员
- ⽤static修饰的成员变量,称之为静态成员变量,静态成员变量⼀定要在类外进⾏初始化。
- 静态成员变量为所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区。
- ⽤static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针。
- 静态成员函数中可以访问其他的静态成员,但是不能访问⾮静态的,因为没有this指针。
- ⾮静态的成员函数,可以访问任意的静态成员变量和静态成员函数。
- 突破类域就可以访问静态成员,可以通过类名::静态成员或者对象.静态成员来访问静态成员变量 和静态成员函数。
- 静态成员也是类的成员,受public、protected、private访问限定符的限制。
- 静态成员变量不能在声明位置给缺省值初始化,因为缺省值是个构造函数初始化列表的,静态成员 变量不属于某个对象,不⾛构造函数初始化列表。
class MyClass {
public:
static void fun() {
// 访问静态成员变量
cout << val<< std::endl;
}
static int val;
};
// 类外初始化
int MyClass::val= 10;
//通过类名访问
MyClass::val = 10;
MyClass::fun();
//通过对象名(不推荐)
MyClass obj;
obj.val= 20;
obj.fun();
3.0 友元
友元是 C++ 中的一种特殊机制,它允许外部的函数或类访问类的私有成员和保护成员。通常情况下,类的私有成员不能被外部直接访问,但通过友元声明,类可以赋予特定的函数或类访问这些成员的权限。
3.1.0 友元函数
代码语言:javascript代码运行次数:0运行复制class QQ {
private:
int _val;
public:
QQ(int val)
: _val(val)
{}
// 声明外部函数为友元
friend void display(const QQ& obj);
};
// 友元函数
void display(const QQ& obj) {
cout << "Private _val: " << obj._val << endl;
}
display函数作为QQ的有元函数被声明,因此可以访问QQ里的私有成员。
外部友元函数可访问类的私有和保护成员,友元函数仅仅是⼀种声明,他不是类的成员函数。
3.2.0 友元类
代码语言:javascript代码运行次数:0运行复制class QQ {
private:
int _val;
public:
QQ(int val)
: _val(val)
{}
// 声明QQFriend类为友元
friend class QQFriend;
};
class QQFriend {
public:
void display(const QQ& obj)
{
// 可以访问 MyClass 的私有成员
cout << "Private _val: " << obj._val << endl;
}
};
QQFriend 被声明为QQ的友元类,QQFriend类可以访问QQ的private。
3.3.0 注意:
- 友元函数可以在类定义的任何地⽅声明,不受类访问限定符限制。
- ⼀个函数可以是多个类的友元函数。
- 友元类中的成员函数都可以是另⼀个类的友元函数,都可以访问另⼀个类中的私有和保护成员。
- 友元类的关系是单向的,不具有交换性,⽐如A类是B类的友元,但是B类不是A类的友元。
- 友元类关系不能传递,如果A是B的友元,B是C的友元,但是A不是C的友元。
- 有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多⽤。
4.0 内部类
⼀个类定义在另⼀个类的内部,这个内部类就叫做内部类。
内部类默认是外部类的友元类。
内部类本质也是⼀种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使⽤,那么可以考 虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的专属内部类,其 他地⽅都⽤不了。
代码语言:javascript代码运行次数:0运行复制class OuterClass {
private:
int _OuterVal;
public:
OuterClass(int val) : _OuterVal(val) {}
// 内部类定义
//默认InnerClass为OuterClass的友元类
class InnerClass {
private:
int _innerVal;
public:
InnerClass(int val) : _innerVal(val) {}
void display(const OuterClass& O) {
cout << "Inner class variable: " << _innerVal << endl;
cout << "Outer class variable: " << O._OuterVal << endl;
}
};
void displayOuter() {
cout << "Outer class variable: " << _OuterVal << endl;
}
};
5.0 匿名对象
匿名对象是指那些没有显式名称的对象,通常在创建时被直接初始化并使用。
匿名对象⽣命周期只在当前⼀⾏,⼀般临时定义⼀个对象当前⽤⼀下即可,就可以定义匿名对象。
5.1.0 临时对象
代码语言:javascript代码运行次数:0运行复制class QQ {
public:
QQ()
{
cout << "QQ()\n";
}
~QQ()
{
cout << "~QQ()\n";
}
};
int main()
{
// 创建匿名对象并直接调用它的构造函数
QQ();
// 程序结束时会调用匿名对象的析构函数
return 0;
}
匿名对象 QQ() 被创建,但没有被保存为任何变量。它在创建后立即被销毁。
5.2.0 临时对象作为函数参数
代码语言:javascript代码运行次数:0运行复制class QQ {
public:
QQ(int value)
{
cout << "QQ(int value) " << value << endl;
}
~QQ()
{
cout << "~QQ()\n";
}
};
void func(QQ obj)
{
cout << "func(QQ obj)\n";
}
int main()
{
func(QQ(10)); // 创建一个匿名对象并传递给函数
return 0;
}
QQ(10)
创建了一个匿名对象,并将其作为参数传递给 func
函数。函数执行完毕后,匿名对象的析构函数被调用。
5.3.0 通过返回值创建匿名对象
代码语言:javascript代码运行次数:0运行复制class QQ {
public:
QQ()
{
cout << "QQ()\n";
}
~QQ()
{
cout << "~QQ()\n";
}
};
QQ func() {
return QQ(); // 返回一个匿名对象
}
int main() {
QQ obj = func(); // 接收匿名对象
return 0;
}
func函数返回了一个匿名对象 QQ()
,并在 main
函数中接收。匿名对象在返回后立即创建并销毁,构造和析构函数被调用。
6.0 拷贝构造时编译器的优化
如何优化C++标准并没有严格规定,各个编译器会根据情况⾃⾏处理。当前主流的相对新⼀点的编 译器对于连续⼀个表达式步骤中的连续拷⻉会进⾏合并优化,有些更新更"激进"的编译器还会进⾏ 跨⾏跨表达式的合并优化。
代码语言:javascript代码运行次数:0运行复制class QQ
{
public:
QQ(int a = 0)
:_a1(a)
{
cout << "QQ(int a = 0)——构造" << endl;
}
QQ(const QQ& aa)
:_a1(aa._a1)
{
cout << "QQ(const QQ& aa)——拷贝构造" << endl;
}
QQ& operator=(const QQ& aa)
{
cout << "QQ& operator=(const QQ& aa)——赋值构造" << endl;
if (this != &aa)
{
_a1 = aa._a1;
}
return *this;
}
~QQ()
{
cout << "~QQ()——析构" << endl;
}
private:
int _a1 = 1;
};
void f1(QQ aa)
{}
QQ f2()
{
QQ qq;
return qq;
}
- 隐式类型,直接构造 + 拷⻉构造-> 优化为直接构造
- 直接构造 + 拷⻉构造-> 优化为直接构造
- 直接构造+拷贝构造+拷贝构造 -> 优化为直接构