初始化列表+static+友元函数+内部类--类和对象5

初始化列表

先看两种不好的初始化
1、b(缺省值)只能初始化为1,aa只能为0

class A
{
public:
	A(int a = 0)//不写=0会显示出错为B没有合适的构造函数可以用,因为B要调用A的默认构造
	{
		_a = a;
	}
	}
private:
	int _a;
};
class B
{
public:
private:
	int _b = 1;  // 内置类型--默认构造函数不能让内置类型初始化
	A _aa;   // 自定义类型
};
int main()
{
	B b;
	return 0;
}

在这里插入图片描述
2、显示的初始化–构造函数体内初始化

class A
{
public:
	A(int a = 0)//调用构造
	{
		_a = a;
		cout << "A(int a = 0)" << endl;
	}
	A& operator=(const A& aa)//调用赋值
	{
		cout << "A& operator=(const A& aa)" << endl;
		if (this != &aa)
		{
			_a = aa._a;
		}
		return *this;
	}
private:
	int _a;
};
class B
{
public:
	B(int a, int b)
	{
	  //有名对象
		//A aa(a);
		//_aa = aa;--赋值的想法
		//匿名对象--作用域只在当前函数
		_aa = A(a);
		_b = b; 
   //_aa.a=a--err,成员变量为私有的不能随便访问或者是修改
	}
private:
	int _b = 1; //内置类型--这里给的是缺省值,这里不是定义
	A _aa;   //自定义类型
};
int main()
{
	B b(10, 20);
	return 0;
}

自定义类型显示初始化的过程调用了哪些函数
在这里插入图片描述
3、初始化列表–重点
在这里插入图片描述在这里插入图片描述

class A
{
public:
	A(int a = 0)
	{
		_a = a;
		cout << "A(int a = 0)" << endl;
	}
	A& operator=(const A& aa)
	{
		cout << "A& operator=(const A& aa)" << endl;
		if (this != &aa)
		{
			_a = aa._a;
		}
		return *this;
	}
private:
	int _a;
};
class B
{
public:
	B(int a, int b)
	//函数体内初始化 -> 自定义类型成员,改用初始化列表初始化,可以提高效率
	  :_aa(a)//直接调用它的构造
	{
		_b = b;
	}
private:
	int _b = 1;  // 内置类型
	A _aa;   // 自定义类型
};
int main()
{
	B b(10, 20);
	return 0;
}

在这里插入图片描述
4、尽量使用初始化列表初始化,有哪些是必须使用初始化列表

  • const类型的成员必须在定义的时候初始化–不能用构造函数体内赋值,要用初始化列表初始化
  • 引用也必须在初始化定义–用初始化列表初始化
  • 无默认的初始化构造函数的自定义类型成员-a–用初始化列表初始化
class A
{
public:
	A(int a)
	{
		_a = a;
		cout << "A(int a = 0)" << endl;
	}
	A& operator=(const A& aa)
	{
		cout << "A& operator=(const A& aa)" << endl;
		if (this != &aa)
		{
			_a = aa._a;
		}
		return *this;
	}
private:
	int _a;//无默认的构造函数
};
class B
{
public:
//初始化列表即成员变量定义的地方
	B(int c, int& ref, int a)//这里的ref为main函数的ref别名,为了让下面_ref改变能传回main函数而用引用,不用引用则引用的是局部变量即非法空间,调用完构造函数栈帧就销毁了,main函数会因为调用非法空间而ref返回随机值
		:_c(c)
		, _ref(ref)
		, _aa(a)
	{//初始化列表可以和函数体内初始化
		//_c = c;--err
		//_ref = ref;--err
		_ref = 100;//在函数里面改变_ref,ref也改变,因为_ref是上面ref的别名
	}
private:
	const int _c;//const
	int& _ref;//引用
	A _aa;//无默认构造函数的自定义类型
};
int main()
{
	int ref = 10;
	B b(1, ref, 1000);
	cout << ref << endl;
	return 0;
}

在这里插入图片描述
5、初始化列表和函数体内初始化可以混合着用
在这里插入图片描述
6、成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中出现的先后次序无关

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();
  } 
  • 输出 a1=1,a2=随机值
  • 先用-a1(此时为随机值)初始化-a2,则-a2为随机值
  • 再用a(值为1)来初始化-a1,则-a1为1
  • 所以列表中初始化顺序和声明的顺序最好保持一致

单参数–两种方式调用都可以

void TestDate()
{ 
  Date d1(2018);  
  d2 = 2019;//隐式转换--通过拷贝构造
}
  • 两行虽然结果一样的,但是直接调用构造函数,但是对于编译器而言过程是不一样的,第二行是优化后的结果

让程序计算,下面程序构造了多少个对象 (构造+拷贝构造)

用countC和countCC来计算,不能使用count,因为std是展开的会出现命名空间的问题

int countC = 0;
int countCC = 0;
class A
{
public:
	A()//构造
	{
		++countC;
	} 
	A(const A& a)//拷贝构造
	{
		++countCC;
	}
};
A f(A a)
{
	A ret(a);
	return ret;
}
int main()
{
	A a1 = f(A());
	A a2;
	A a3;
	a3 = f(a2);
	cout << countC << endl;
	cout << countCC << endl; 
	return 0;
}

在这里插入图片描述

static的作用

1、上面算count的程序可以在main函数里countC=1就能更改countC的值,所以要使用static

class A
{
public:
	A()
	{
		++_count;
	}
	A(const A& a)
	{
		++_count;
	}
private:
	int _a;// 存在定义出的对象中,属于某个对象
	//跟全局变量比较,他受类域和访问限定符限制,更好体现封装,别人不能轻易修改它
	static int _count;//存在静态区,属于整个类,也属于每个定义出来的对象共享	--声明
};
//定义初始化
// 静态成员变量不能在构造函数初始化,在全局位置定义初始化--不在这写会报错
int A::_count = 0;
int main()
{
	 cout << sizeof(A) << endl;//这里算的是A中定义出来的对象的大小
	//public情况下才能输出下面--属于整个类,也属于每个定义出来的对象共享
	//A::_count = 10;//因为封装了所以在这里就不能修改了--err
	//cout << A::_count << endl;
	//cout << a1._count << endl;
	//cout << a2._count << endl;
}
  • 只算_a的值,不算static的
    在这里插入图片描述
    2、私有的成员不能访问–写一个公有的构造函数来访问
class A
{
public:
	A()
	{
		++_count;
	}
	A(const A& a)
	{
		++_count;
	}
	// 静态成员函数没有this指针
	static int GetCount()
	{
		//_a = 1;--err因为它没有this指针
		return _count;
	}
private:
	int _a;	//sizeof(A)	算的是它
	static int _count;//静态变量不能再这里给缺省值,因为静态成员不在构造函数初始化,要在类外面全局位置初始化
	// 声明
};
int A::_count = 0;
A f(A a)
{
	A ret(a);
	return ret;
}
void test()
{
	A a1 = f(A());
	A a2;
	A a3;
	a3 = f(a2);
}
int main()
{
	test();
	//1、有名对象
	//A ret;
	//cout << ret.GetCount() - 1<< endl;//-1是减去自己定义的
	//2、匿名对象 --现在类放在test而不是放在main函数里所以只能用匿名对象
	//cout << A().GetCount() - 1<< endl;
	//3、使用静态成员函数--不需要使用对象来调用,通过类域就可以了
	 cout << A::GetCount() << endl;
	//cout << A().GetCount()-1 << endl;//静态函数也可以通过对象来访问
	return 0;
}

在这里插入图片描述
static的作用–重点

  • c
  • 1、修饰全局变量和全局函数,改变链接属性,只在当前文件可见
  • 2、修饰局部变量,改变声明周期
  • 上面的特性在C++中依旧有用,C++兼容c的这些特性
  • cpp
  • 1、修饰成员变量和成员函数,成员变量属于整个类,所有对象共享,成员函数没有this指针
    在这里插入图片描述

友元函数

1、破坏了封装,能不用就别用
2、友元函数不能用const修饰
3、普通函数、静态成员函数–不能使用const修饰,只有非静态的成员函数 才能用const修饰,因为const修饰的其实是this指针指向的对象

class Date
{
	// 友元函数--C++在类外不能访问private需要写公有构造函数,但友元函数可以提供访问private的权力
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);
public:
	Date(int year = 0, int month = 0, int day = 1)//重载初始化
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	//不能写成成员函数
	/*void operator<<(ostream& out)
	{
		out << _year << "/" << _month << "/" << _day << endl;
	}*/
private:
	int _year;
	int _month;
	int _day;
};
// 为了让cout在第一个参数,左操作数,我们就只能写成全局的
// 其次就是operator<<搞成友元,可以在类中访问私有。
// 但是operator<<(流插入)不是必须友元,还有其他方式
ostream& operator<<(ostream& out, const Date& d)//要有返回值和用引用返回是因为考虑连续输出情况
{
	out << d._year << "/" << d._month << "/" << d._day << endl;
	return out;
}
istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}
int main()
{
	Date d1, d2, d3;
	//d1.operator<<(cout);
	//d1 << cout;
	// 运算符重载,运算符有几个操作数,重载函数就有几个参数,左操作数是第一个参数,右操作数是第二个参数
	// operator<<写成成员函数,this指针默认占据了第一个位置,对象(d1)就要做左操作数(但是我们需要cout做左操作数)
	// 那么用起来就不符合流(对象)特性,虽然可以用,但是不符合运算符原来的用法和特性--所以不能写成成员函数得放到类外面
	cin >> d1 >> d2;
	cout << d1 << d2;
	return 0;
}

2、一个函数可以是多个类的友元函数

// 前置声明
class B;
class A
{
	friend void f(const A& aa, const B& bb);
public:
	A(int a = 0)
		:_a(a)
	{}
private:
	int _a;
};
class B
{
	friend void f(const A& aa, const B& bb);
public:
	//B()
	//	:_p(nullptr)
	//	, _aa(100)
	//{}
private:
	// 给的缺省值,这里不是定义,
	// 这里只是声明,所以这里不是初始化
	int _b = 0;
	int* _p = (int*)malloc(sizeof(int) * 10);
	//A _aa = A(10);先构造再拷贝构造
	A _aa = 10;
	// 静态变量不能在这里给缺省值,因为静态成员不在构造函数初始化,要在类外面全局位置定义初始化
	//static int _n = 10;--err
};
void f(const A& aa, const B& bb)
{
	cout << aa._a << endl;
	cout << bb._b << endl;
}
int main()
{
	A aa;
	B bb;
	f(aa, bb);
	return 0;
}

在这里插入图片描述
在这里插入图片描述
3、友元类

  • 元关系是单向的,Date是Time的友元,在Date类中可以使用对象访问Time的私有保护成员,但是Time不是Date的友元,在Time类中不可以使用对象访问Date的私有保护成员
  • 友元不具有传递性
class Time
{
	friend class Date;   // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量
public:
	Time(int hour = 0, int minute = 0, int second = 0)
		: _hour(hour)
		, _minute(minute)
		, _second(second)
	{}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}

	void SetTimeOfDate(int hour, int minute, int second)
	{
		// 直接访问时间类私有的成员变量
		_t._hour = hour;
		_t._minute = minute;
		_t._second = second;
	}
private:
	int _year;
	int _month;
	int _day;
	Time _t;
};
int main()
{
	return 0;
}

内部类–在类中定义类

  • 在类A中写类B,B天生就是A的友元,则B可以直接访问A的成员
  • sizeof(外部类)–只算外部类的大小与内部类无关
class A
{
private:
//声明
	static int k;
	int h;
public:
	class B // B天生就是A的友元,则B可以直接访问A的成员
	{
	public:
		void foo(const A& a)
		{
			cout << k << endl;//OK
			cout << a.h << endl;//OK
		}
	private:
		int _b;
	};
};
//定义
int A::k = 0;
int main()
{
	 cout << sizeof(A) << endl;
	// 这里计算A类型对象大小的时候,不考虑。因为B作为A的内部类,跟普通类并没有什么区别,
	// 只是定义在A的内部,他受到A的类域的限制和访问限定符的限制
	A aa;
	A::B bb;
	//B直接访问A的成员
	bb.foo(aa);
	return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值