类与对象 (下)

 

一、<<(流插入) 重载及友元函数初学

我们经常用cout << 10<< endl;输出的结果是10

那么我们可以打印我们自定义的对象吗?

当然可以,只需要我们重载一下流插入成员函数即可.

void Date::operator<<(ostream& out)//cout是一个ostream类型的对象
{
	out << _year <<" --" << _month << "--" << _day << endl;
}
int main()
{
    d1.operator<<(cout);//&d1,cout
	d1 << cout;//为什么不写成cout<<d1;因为因为是成员函数,&cout是this指针,而d1是参数,就与成员函数参数不对应
}

但是d1<<cout;又不符合我们所学的习惯,因此有第二种方法,可以不讲<<重载函数写成成员函数.可以直接将它写成全局的函数.
但是此时,我们我们无法调用类中的私有成员变量

此时,就引入了一个新的访问私有成员变量的方法:友元函数

 即在类中输入

friend void operator<<(ostream& out, const Date& d1);

即让类知道,我是你的朋友,即可访问里面的私有成员变量了

 此时又出现了一个问题,我们平常的cout时可以连续输出的,那么这里可以吗?

这里时不可以的,因为cout<<d1,返回为void,void << d2,无法再打印

那么解决的办法就是将void该为ostream&,return cout,那么打印完d1后,返回cout,既可以继续打印cout<<d2了.

ostream& operator<<(ostream& out, const Date& d1)
{
	out << d1._year << " --" << d1._month << "--" << d1._day << endl;
	return out;
}

 相应的流提取重载:

istream& operator>>(istream& in, Date& d1)
{
	in >> d1._year >> d1._month >> d1._day;
	return in;
}

二、初始化列表

在创建对象时,编译器会调用各自的构造函数进行初始化,虽然上述对象中已经有了一个初始值,但是这只能称之为赋值,并不能称之为初始化,因为初始化只能有一次,而构造函数体内可以赋值多次,并且当声明成员变量时,const int k ;无法再定义,所以因为这些原因我们引入了初始化列表的概念:

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。

class Date
{
private://声明
	int _year;
	int _month;
	int _day;
	const int k;

public:

	//初始化列表:成员变量初始化的地方
	Date(int year, int month, int day)
		:_year(year)//定义的时候就定义
		, _month(month)
		, _day(day)
		, k(10)
	{
        //定义在里面,就是先定义后给值,比如_year等定义在外面和里面都可以
    }
};

int main()
{
	Date d1(2022,8,3);
}

此时即可初始化带const的成员变量。

注意:

(1)每个成员变量在初始化列表中初始化一次

(2)类中的①引用成员变量②const成员变量③自定义成员(该类没有默认构造函数)的必须放在初始化列表位置进行初始化


总结:

内置类型的成员,在函数体和在初始化列表初始化都可以

自定义类型的成员,建议在初始化列表初始化,这样更高效

2.1 牛刀小试

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 随机值

 解析:

此题选择D选项,用到的是一个语法:成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关

三、 隐式类型转换

 隐式类型转换这种方式让我们就感觉有些突兀,所以C++又引入了一个关键字"explicit",用explicit修饰构造函数,用来禁止单参构造函数的隐式转换:

四、static

声明为 static 的类成员 称为 类的静态成员 ,用 static 修饰的 成员变量 ,称之为 静态成员变量 ;用 static 修饰 成员函数 ,称之为 静态成员函数 静态的成员变量一定要在类外进行初始化

 

 特性:

 

 4.1  牛刀小试

求1+2+3+...+n_牛客题霸_牛客网

class sum
{
public:
    //默认构造函数
    sum()
    {
        i += j;
        j++;
    }
    static int getsum()
    {
        return i;
    }
private:
    static int i;
    static int j;
};
int sum::i = 0;//用来计数
int sum::j = 1;

class Solution {
public:
    int Sum_Solution(int n) 
    {
        //创造n个sum类的数组,每实例化一个数组就会调用构造函数为他初始化一次
        sum a[n];//变长数组
        //return sum().getsum() -(n+1);//匿名类,相当于又创建了一个实例,所以又增加了一个构造
        return sum::getsum();
    }
};

五、 C++11的补丁

下面的代码:默认的构造函数对内置类型不进行初始化,但是对于自定义类型会去调用它的默认构造函数,这是之前所学的,这就让我们觉得有些不方便,所以C++11对此做了一个补丁

 补丁就是在成员变量声明的时候给变量一个缺省值

但如果默认成员变量给_a进行了初始化,那么这个缺省值就不会有效,如果写了默认成员函数,但是初始化列表没有对他进行初始化,也会默认为缺省值

 成员变量可以定义缺省值的种类

class B
{
public:
	B(int a)
	{
		_a = a;
	}
private :
	int _a;
};

class A
{
public:
	A(int a)
	{
		_a = a;
	}
private:
	int _a = 0;//这里不是初始化,因为这里是声明,给成员变量的是缺省值
	B _b = 10;
	B _b1 = B(20);
	int* p = (int*)malloc(4*10);
	int arr[10] = {1,2,3,4,5};

	//静态成员变量不能这样给缺省值,必须在类外面全局位置定义初始化
	/*static int _count = 0;*///err
};
int A::_count = 0;
int main()
{
	A d1(1);
}

六、友元函数

友元分为: 友元函数 友元类
友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
(1) 友元函数 可访问类的私有和保护成员,但 不是类的成员函数 ,一般就是全局函数
(2) 友元函数 不能用 const 修饰
(3)友元函数 可以在类定义的任何地方声明, 不受类访问限定符限制
(4)一个函数可以是多个类的友元函数
(5)友元函数的调用与普通函数的调用和原理相同

6.1友元类

 

(1)友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
(2)友元关系是单向的,不具有交换性, 比如上述 Time 类和 Date 类,在 Time 类中声明 Date 类为其友元类,那么可以在 Date 类中直接访问 Time
(3)类的私有成员变量,但想在 Time 类中访问 Date 类中私有的成员变量则不行。
(4)友元关系不能传递
如果 B A 的友元, C B 的友元,则不能说明 CA 的友元。

七、内部类

概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。注意此时这个内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去调用内部类。外部类对内部类没有任何优越的访问权限。

注意:内部类就是外部类的友元类。注意友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。

1.内部类B和在全局定义是基本一样的,只是它受外部类A的限制,只是定义在A的类域中,内部类可以定义在外部类的publicprotectedprivate都是可以的
2.内部类B天生就是外部类A的友元,也就是B中可以访问A的私有,A不能访问B的私有,内部类可以直接访问外部类中的static、枚举成员,不需要外部类的对象/类名。

3.sizeof(外部类)=外部类,和内部类没有任何关系

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值