目录
友元
友元函数
友元类
内部类
匿名对象
拷贝对象时的一些编译器优化
再次理解类和对象
友元
基本概念:友元提供了一种突破封装的方式,有时提供了便利,但是友元会增加耦合度,破坏了封装,所以友元不宜多用(开后门)
格式:friend 函数声明
分类:友元函数和友元类
友元函数
解决问题:类外无法访问成员函数
注意事项:
1、友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字
class Date
{
friend ostream& operator<<(ostream& _cout, const Date& d);
friend istream& operator>>(istream& _cin, Date& d);
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{
_cin >> d._year;
_cin >> d._month;
_cin >> d._day;
return _cin;
}
int main()
{
Date d;
cin >> d;
cout << d << endl;
return 0;
}
2、友元函数可以访问类的私有和保护成员,但不是类的成员函数
3、友元函数不能用const修饰(没必要)
4、友元函数可以在类定义的任何地方声明,不受类的访问限定符限制
5、一个函数可以是多个类的友元函数
6、友元函数的调用与普通函数的调用原理相同
7、不想用友元就用get和set
友元类
注意事项:
1、友元的关系是单向的,不具有交换性(你是我的朋友我允许你看我,但不知道你让不我看你)
Time类中声明Date类是其友元类,则可以在Date类中访问Time类的私有成员变量,但想在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;
};
2、友元关系不能传递(C是B的友元,B是A的友元,在未声明的情况下C不是A的友元)
3、友元关系不能继承
4、友元关系是双向时,两个类都可以访问对方的成员变量和成员函数
内部类
基本概念:一个类定义在另一个类的内部,这个内部类就叫内部类
注意事项:
1、类不占用空间(编译后不占用空间)
#include <iostream>
using namespace std;
class A
{
public:
class B
{
private:
int _b1;
};
private:
int _a1;
int _a2;
};
int main()
{
cout << sizeof(A)<<endl;
return 0;
}
想象中在A类中嵌套一个B类应该是下的代码是这样的,但实际上不是:
2、内部类受外部类的类域的限制
3、内部类是外部类的友元(内部类可以访问外部类的成员,但是外部类不能访问内部类的成员)
4、内部类可以定义在外部类的任意位置
5、内部类可以直接访问外部类中的static成员,不需要外部类的对象或类名
#include <iostream>
using namespace std;
class A
{
private:
static int k;
int h;
public:
class B // B天生就是A的友元
{
public:
void foo(const A& a)
{
cout << k << endl;//OK,this->k
cout << a.h << endl;//OK,this->h
}
};
};
int A::k = 1;
int main()
{
A::B b;
b.foo(A());
return 0;
}
A()
:匿名的临时对象b.foo(A()):
将匿名对象作为参数传递给了类 A 中嵌套类 B 的成员函数 foo()
6、sizeof(外部类) = 外部类的大小,和内部类没有任何关系
匿名对象
格式:类名()
注意事项:
1、匿名对象的括号内可以有参数
2、匿名对象的声明周期只在当前一行(第1行定义匿名对象,第2行时该匿名对象销毁)
#include <iostream>
using namespace std;
class A
{
public:
A(int a = 0)
:_a(a)
{
cout << "A(int a)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
class Solution
{
public:
int Sum_Solution(int n)
{
//...
return n;
}
};
int main()
{
A aa1;//有名对象
A();//匿名对象
A(10);//匿名对象
A aa2(2);//有名对象
Solution().Sum_Solution(10);
return 0;
}
3、匿名对象可以提供一些便利(当我们只是向调用对象的内容时有名写两行,匿名写一行)
//形式一
Solution s1;
s1.func(10);
//形式二
Solution().Sum_Solution(10);
拷贝对象时的一些编译器优化
基本概念:在传参和传返回值时,一般编译器会做一些优化,减少对象的拷贝
连续构造 + 拷贝构造 = 优化为直接构造
连续构造 + 拷贝构造 = 优化为一个构造
连续拷贝构造 + 拷贝构造 = 优化为一个拷贝构造
连续拷贝构造 + 赋值重载 = 无法优化
再次理解类和对象
计算机不认识现实生活中的实体,只认识二进制格式的数据,如果想要计算机认识现实中的实体,用户必须通过某种面向对象的语言,对实体进行描述,然后通过编写程序,创建对象后计算机才可以认识,比如像要让计算机认识洗衣机就需要:
- 用户先对现实中的洗衣机实体进行抽象认知,即在思想层面对洗衣机进行认识,洗衣机有什么属性(成员变量)和功能(成员函数)
- 此时,人脑中已经对洗衣机有了一个较为清醒的认识,通过某种面相对象的语言将洗衣机用类来进行描述,就可以让计算机知道人脑中对洗衣机的认识
- 然后,在计算机中就有了一个洗衣机类,但它只是站在计算机的角度对洗衣机进行描述的,只有利用洗衣机类实例化出具体的洗衣机对象,用户才可以模拟现实中洗衣机实体的功能
#include <iostream>
#include <string>
class WashingMachine {
private:
int capacity; // 洗衣容量
bool isOn; // 洗衣机是否开启
std::string brand; // 品牌
public:
WashingMachine(int cap, const std::string& b) : capacity(cap), isOn(false), brand(b) {}
void turnOn() {
isOn = true;
std::cout << "Washing machine turned on." << std::endl;
}
void turnOff() {
isOn = false;
std::cout << "Washing machine turned off." << std::endl;
}
void washClothes() {
if(isOn) {
std::cout << "Washing clothes..." << std::endl;
} else {
std::cout << "Please turn on the washing machine first."<<std:endl;;
}
}
};
int main() {
WashingMachine myWasher(5, "ABC");
myWasher.turnOn();
myWasher.washClothes();
return 0;
}
~over~