C++ 类和对象下

文章详细讨论了C++中重载operator<<时的顺序问题以及const对象如何调用成员函数。介绍了初始化列表的重要性,特别是在处理引用、const成员和自定义类型时。同时,文章提到了隐式类型转换在构造函数中的应用,以及如何通过使用explicit关键字避免不必要的转换。此外,还讲解了静态成员变量的特性及其与非静态成员函数的交互。最后,文章给出了如何设计类以限制对象在栈上或堆上创建的例子。
摘要由CSDN通过智能技术生成

重载operator<< 输出自定义类型

类内重载导致操作数顺序颠倒问题:

在这里插入图片描述
在这里插入图片描述

所以定义在类外,但又要访问私有成员变量,就利用友元突破访问限制
在这里插入图片描述

在这里插入图片描述
类外定义利用cout这个对象的别名,输出对象的成员变量,并且需要返回这个cout的别名,因为对于连续输出需要从左到右返回cout的顺序问题 cout<< x << y <<endl;
在这里插入图片描述

const对象 无法调用 非const成员函数

原因:权限可以缩小,但不能放大
在这里插入图片描述
结论:只要是不修改this指针指向的对象内容的成员函数,都可以加上const
好处是 非const和const的对象都可以调用,但对于要修改this指向内容的对象加上const就没法改了

非const函数的缺陷

bool operator(const Date& x) 没加const 导致d2 < d1 等价于 d2.operator(&d2,d2),此时this指针类型是const Date* 是一种权限的放大,所以结合结论,不改变成员变量的函数需要加const
在这里插入图片描述

初始化列表:对象的成员变量定义的位置

熟悉的玩法是构造体函数赋值

class Date
{
public:
 Date(int year, int month, int day)
 {
 _year = year;
 _month = month;
 _day = day;
 }
private:
 int _year;
 int _month;
 int _day;
};

虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化,
构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值

无论怎样最终都会走到初始化列表
在这里插入图片描述

即使是没用初始化列表,对象的成员变量仍然会走初始化列表

为什么会要用到初始化列表?

构造体赋值无法解决 在定义的时候就得初始化的变量
在这里插入图片描述

在这里插入图片描述

特征:必须在定义的时候初始化
如:引用成员变量
const成员变量
自定义类型成员(且该类没有默认构造函数时)(也必须在定义时初始化,因为需要传参)(如果有默认构造函数,就不需要传参,初始化列表可以初始化,也可以不初始化,不初始化就自动调用默认构造函数,就看你想不想显示初始化了)

自定义类型成员如何初始列表初始化

在这里插入图片描述

灵活的初始化列表

不写初始化列表,所有的成员也会走初始化列表,因为初始化列表是成员定义的地方,它至少会出现一次,则_pushst和_popst就去调用它们的默认构造,如果想显示的初始化对象同样也可以在初始化列表里面传参去初始化
在这里插入图片描述

声明次序

成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关

class A
{
public:
 A(int a)
 :_a1(a)
 ,_a2(_a1)
 {}
 
 void Print() {
 cout<<_a1<<" "<<_a2<<endl;
 }
private:
 int _a2;
 int _a1;
};
int main() {
 A aa(1);
 aa.Print();
}
A. 输出1 1
B.程序崩溃
C.编译不通过
D.输出1 随机值

初始化顺序:
private:
int _a2;
int _a1;
先初始化_a2,再_a1,导致_a2是个随机值

内置类型_size不给缺省值,仍然会走初始化列表,给了一个随机值

在这里插入图片描述

隐式类型转换与编译器优化连续构造

#include <iostream>
using namespace std;
class A 
{
public:
	A(int a)
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}
	A(const A& aa)
		:_a(aa._a)
	{
		cout << "A(const A& aa)" << endl;
	}
private:
	int _a;
};

int main()
{
	A aa1(1);
	A aa2 = 2;

	return 0;
}

A aa2 = 2;能成功是因为构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数,还具有类型转换的作用。
A aa2 = 2; 发生隐式类型转换,2作为参数传给构造函数,构造成的临时变量是A类型,再拷贝构造给A aa2
但是连续的构造会被编译器优化成用2直接构造

类比 double 和 int 的临时变量
在这里插入图片描述

证明确实开辟了临时空间?

在这里插入图片描述
如果没开临时空间直接构造,那么不需要加const修饰 A& aa3 = 2 就能用
实际上确实开了临时空间,而临时空间具有常性,所以需要加const

隐式类型转换的好处

在这里插入图片描述

explicit 禁止止构造函数的隐式类型转换

在这里插入图片描述

static

混淆点:函数声明和定义及变量的定义和声明

int i  //变量的定义,未初始化

extern int i //变量的声明

静态成员变量

成员变量与静态成员变量的区别
在这里插入图片描述
static int _scoun是声明,既然不属于对象,不存储在对象里面,也就无法初始化列表去初始化
它是一个全局的变量,要在类外定义
在这里插入图片描述

可以解决定义全局变量的劣势:任何地方都可以随意改变,定义成静态成员变量,只要突破类域和访问限定符就可以访问,权限改成public和全局的差不多。

权限改私有 只能通过公有的成员函数来返回静态成员变量,但是必须有一个对象,在无对象时要访问就必须将公有成员函数改成静态的,做到不用对象就可以调用

在这里插入图片描述

【成员函数静态和非静态之间调用问题】

  1. 静态成员函数可以调用非静态成员函数吗?

  2. 非静态成员函数可以调用类的静态成员函数吗?

【回答】

在这里插入图片描述

设计一个类,在类外面只能在栈上创建对象

设计一个类,在类外面只能在堆上创建对象

将构造函数权限改为私有,导致类外创建对象的话栈,静态,堆都创建不了,但是可以调用相应的公有成员函数创建对象(私有成员(变量和函数)在类外不能访问但是类内的函数随便调用),但是调用又需要一个对象,所以只能改为静态,突破类域和访问限定符就可以访问

// 设计一个类,在类外面只能在栈上创建对象
// 设计一个类,在类外面只能在堆上创建对象
class A
{
public:
	static A GetStackObj()
	{
		A aa;
		return aa;
	}

	static A* GetHeapObj()
	{
		return new A;
	}
private:
	A()
	{}

private:
	int _a1 = 1;
	int _a2 = 2;
};

int main()
{
	//static A aa1;   //  静态区
	//A aa2;          //  栈 
	//A* ptr = new A; //  堆
	A::GetStackObj();
	A::GetHeapObj();

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值