文章目录
“>>”:流提取运算符
“<<”:流插入运算符
引用
取别名的过程中,权限可以缩小但是不能够放大。
int main()
{
int a = 0;
int& b = a;
int* p = &a;
//应用取别名的过程中,权限可以缩小但是不能放大
//加上const是权限的缩小
const int c = 10;
const int& d = c;
const int& f = a;
double dd = 1.1;
//类型转换,中间都会产生了临时变量
//dd->临时变量->ii
int ii = dd;
//rii引用的是临时变量,而临时变量具有常性,所以采用const int &
const int& rii = dd;
//函数传参和返回的时候都会产生临时变量
return 0;
}
在语法层面上,引用是不开空间,仅仅是对变量取别名.指针是开空间,存储变量地址.
底层汇编实现时,引用就是用指针实现的.传值返回,类型转换会产生临时变量,再赋值给别人。
auto
自动推导变量类型.不能作为函数的形参类型,不能直接声明数组.
int x = 10;
auto a = &x;
auto* b = &x;//int*
auto& c = x;
cout << typeid(a).name() <<"->" << typeid(b).name() << "->" << typeid(c).name() << endl;
const int x1 = 10;
auto y = x1;
const auto z = x1;
cout << typeid(y).name() << endl;//int
cout << typeid(z).name() << endl;//int
//语法糖,范围for 必须是数组名
for(auto e:array)//依次将数组中的数字赋值给e
{
cout<<e<<endl;
}
//想修改数组元素值
for(auto& e:array)
{
cout<<e<<endl;
}
数组传参的过程中,数组名会退化为相应的指针类型,但是范围for中必须是数组名.
void TestForFor(int a[])
{
for (auto e : a)//a为指针类型会报错
{
cout << e << endl;
}
}
int main()
{
int a[] = {1,2,3,4,5};
TestForFor(a);
}
NULL&nullptr&0
极端场景下回存在调用的歧义,所以C++推荐使用nullptr
类型是int*,C++11作为关键字引入的.sizeof(nullptr)与sizeof(void*)
占用的字节数相同.
类和对象
兼容C里面结构体struct的用法,也升级为了类.
struct A
{
int year;
int day;
int month;
char name[10];
};
int main()
{
struct A a;//C结构体用法
A a1; //升级为类的用法
strcpy(a1.name,"李四");
strcpy(a.name,"张三");
cout << a.name << endl;
cout << a1.name << endl;
}
struct 默认是公有的,class默认是私有的。
C语言,方法和结构体变量是分离的.
- 面向对象编程的三大特性:封装,继承,多态.
封装:数据和方法放在一起&&访问限定符实现的封装.
class Stack
{
int _top;
int* _a;
int _cap;
public:
void InitStack()
{
_top = _cap = 0;
_a = nullptr;
}
void Push()
{}
void Pop()
{}
int Top()
{
assert(_top);
return _a[_top];
}
};
class B
{
public:
void F()
{}
};
int main()
{
B b;
b.InitB("王五");
cout<<sizeof(Stack)<<endl;//12
cout<<sizeof(B)<<endl;//空类会给一字节,不存储有效数据,只是占位表示类存在
}
- 为什么是
12
?
对象的里面只考虑成员变量,不考虑成员函数。成员函数放到一个公共代码区.
类的实例化
对象的里面只考虑成员变量,不考虑成员函数.数据存放在类实例化的对象里面,图纸不能存,房子里面才能住人啊.函数不在对象里面存储.
不同的对象调用的是类的同一个成员函数,不同的对象实例化的时候设置不同的成员变量。
但是在函数中如何会找到不同对象的成员变量?隐藏的this 指针。
class Date
{
private:
int _year;
int _month;
int _day;
public:
//void Init(Date* this,int y, int m, int d)
void Init(int y, int m, int d)
{
_year = y;
_month = m;
_day = d;
}
//void Print(Date* this)
void Print()
{
cout << _year << "-" << _month << "-" <<_day<< endl;
cout << this->_year << "-" << this->_month << "-" <<this->_day<< endl;
}
};
int main()
{
Date d;
d.Init(2023, 1, 7);
d.Print();
Date d1;
d1.Init(2023, 1, 4);
d1.Print();
return 0;
}
-
函数调用的时候隐藏传对象的地址,形参隐藏接收this指针。但是需要注意:
1.调用成员函数时,不能显示的传实参。
2.定义成员函数时,不能显示的写形参。void Print(Date* this)显示写是错的
3.在成员函数内部,可以写this。类里面有些场景是需要this的。this->_year
4.在调用的成员函数中,this指针是不可修改的,因为对象的调用过程中已经确认了,是右值。指针不可修改,但是指针指向的成员变量是可以修改的。
5.this指针是一个形参,存在于栈空间中。有些编译器会放到寄存器当中,例如vs2013,ecx当中。
const在指针类型之前,是指针指向的内容是不可更改的,限制只读。
const在指针类型之后,是指针不可更改,指向的对象的自身内容是可以更改的。
-
违反语法是编译报错。检查出空指针是运行时错误。
空指针调用成员函数不会编译报错,因为这不是语法错误,编译器检查不出来。
也不会出现空指针访问,因为调用成员函数没有存在对象里面,在一个公共代码区.会把实参传给隐藏的this指针,在没有进行空指针的解引用中,不会出错。传过来个空指针没问题,只要不解引用成员变量就行。
class A { public: void Show() { cout << "show" << endl; } }; class B { int _a; public: void PrintA() { cout << _a << endl;//崩溃在this->_a是nullptr->_a空指针了 } }; int main() { A* p = nullptr; p->Show();//正常运行完毕 B* p1 = nullptr; p1->PrintA();//崩溃 }
默认成员函数
C语言写完之后,忘记初始化造成成员崩溃,忘记清理资源导致可用资源很少.所以提供了默认成员函数.
特点:如果我们不实现,系统会自己生成。
构造函数
他是特殊的成员函数,对象定义出来流自动调用,确保创建的对象都被初始化了.
初始化对象,并不是创建对象开空间。
函数名和类名相同,没有返回值
对象实例化的过程中自动调用,构造函数可以重载。
推荐实现全缺省或者半缺省。语法上无参的和全缺省的函数可以同时存在,但是如果存在无参的调用就会存在二义性出错。
如果类里面没有显示的构造函数,编译器会自动生成一个无参的,但是没有初始化,而且你还不能调试进去.
如果定义了,就不会再生成。
- 那么为什么要有这种机制呢?
C++里面把类型分成内置类型和自定义类型
内置类型:int char 以及内置类型数组.自定义类型:struct、class定义的类型。
我们不写编译器默认生成的构造函数,对于内置类型不做初始化处理, 对于自定义类型去调用他的默认构造函数(不用参数就可以调用)包括全缺省初始化,如果没有默认构造函数就会报错。
默认构造函数:全缺省,手动写无参的,编译器默认生成的(不处理内置类型成员变量只处理自定义类型成员变量)这三个版本都可以.默认构造函数只能有一个.
class Date
{
private:
int _year;
int _month;
int _day;
public:
//全缺省
Date(int y = 1, int m = 1, int d = 1)
{
_year = y;
_month = m;
_day = d;
}
//如果用这个,就不会生成默认构造函数就会导致没有默认构造函数可用而报错
Date(int y, int m, int d)
{
_year = y;
_month = m;
_day = d;
}
};
class A
{
private:
int _a;
Date dd;
public:
};
int main()
{
A a;
析构函数
对象在销毁时也就是出了作用域生命周期结束时,自动调用析构函数完成对象的一些资源清理工作
析构函数名是在类名前面加~
对象是创建在main函数的栈空间上面的,对象的销毁就是栈帧的销毁,不是析构函数管的
对象的this指针指向的资源是需要析构函数释放的
析构的顺序是相反的。
并不是所有的类都需要写析构函数,所以不实现析构函数也可以.
系统会自己生成析构函数
如果我们不写默认生成的析构函数和构造函数类似, 对于内置类型成员变量不作处理,对于自定义类型调用他的析构函数.
自己动态开辟的资源,都需要自己写析构函数清理资源。避免误杀,所以语言不能设计成自动释放.
class Stack
{
int* _a;
size_t _top;
size_t _cap;
public:
Stack(int capacity = 4)
{
_a =(int*) malloc(sizeof(int) * capacity);
if (_a == nullptr)
{
cout << "malloc failed" << endl;
return;
}
_top = 0;
_cap = capacity;
}
~Stack()
{
free(_a);
_a = nullptr;
_top = _cap = 0;
}
};
拷贝构造函数
和构造函数构成重载,用一个同类型的对象初始化我.
-
拷贝构造需要引用
调用拷贝构造,需要先传参数,传值传参又是一个拷贝构造,拷贝构造又需要先传参,但是传值传参还是拷贝构造,无线套娃.
所以当加上引用操作时,形参就是实参的一个别名,就不会调用拷贝构造了,避免递归套娃.-
首先解决,为什么传值传参是一次拷贝构造?代码调试可知,传参之前,先进行一次拷贝构造,然后再调用f()函数给形参赋值.如果加上&,就直接走f()函数了,因为是一个别名.
class Date { private: int _year; int _month; int _day; public: Date(int y = 1, int m = 1, int d = 1) { _year = y; _month = m; _day = d; } Date(const Date& d) { _year = d._year; _month = d._month; _day = d._day; } ~Date() {} }; void f(Date d)//& {} int main() { Date d(2023,1,7); f(d); }
-
如果没有显示定义,系统会生成一个默认的拷贝构造函数.
- 默认生成拷贝构造函数:
内置类型的成员,会完成按字节序的拷贝(浅拷贝)所有内容都一样的拷贝给你。有的类就没必要自己写,用默认生成的就可以,比如日期类里面都是内置类型.如果动态开辟了一块空间,那么浅拷贝之后,int *ptr _a地址相同,两个对象指向的都是一块空间,调用个析构函数就会连续释放两次.
深拷贝就得自己实现拷贝构造。自定义类型成员会调用他的拷贝构造。拷贝构造我们不写生成的默认构造函数,对于内置类型和自定义类型都会进行拷贝处理。但是处理细节是不一样的。
运算符的重载
默认C++是不支持自定义类型对象使用运算符的.
函数名:operator操作符。返回类型看操作符运算之后返回值是什么.
参数,操作符有几个操作数就有几个参数。
不能通过连接其他符号来创建新的符号类型
对于内置类型的运算符不能改变其含义
.* :: sizeof ?: .
注意以上5个运算符是不能进行重载的
不能被重载的运算符只有5个, 点号. 三目运算?: 作用域访 问符:: 运算符sizeof 以及.*
赋值运算符的重载
连续赋值,就需要赋值构造有返回值然后向前赋值操作,返回值就涉及到传值返回,还是传引用返回.传值返回会有一份拷贝,调用一次拷贝构造返回。有几个连续赋值操作,就减少了几次因为返回值的拷贝构造次数.
参数类型----返回值----检测是否给自己赋值操作----返回*this----如果我们不写赋值重载会自动生成一个:编译器默认生成的赋值重载和拷贝构造做的事情完全类似:
- 1.内置类型成员,浅拷贝
- 2.自定义类型会调用operator
两个已经存在的对象之间是赋值重载,有一个还没创建的对象就是拷贝构造.
拷贝构造次数编译器优化
class A
{
public:
A()
{}
A(const A & d)
{
cout<<"A(const A & d)"<<endl;
}
A& operator=(const A &)
{
cout<<"A& operator=(const A &)"<<endl;
return *this;
}
};
void F(A a)
{
return a;
}
int main()
{
A a;
A b=f(a);
//传值的时候,那个对于形参的拷贝,返回值的时候,传值返回又是一次拷贝构造,返回值再赋值给别人b的时候,又是一次拷贝构造,而一次调用里面,连续构造函数会被编译器优化合二为一,将上述二三进行合并。
A c;
A d;
d=F(c);
//如果d是已经存在的对象就无法实现优化了,因为是一次赋值操作,不是连续的构造函数的了.
A();//匿名对象
//匿名对象生命周期只在这一行,过了这一行就调用析构函数。同时实现拷贝构造次数的减少,构造和拷贝构造给形参合二为一
F(A());//传参传匿名对象
}
传参和传返回值的连续构造的过程当中,存在优化的过程。接收与否都是存在返回值的,返回值就是生成的临时变量不接受仍然存在一次拷贝构造。传值返回生成的临时变量和匿名对象相当于一回事,同样进行优化。
//下面的例子,拷贝构造是9次但是优化为了7次
class A
{
public:
A()
{
cout << "A()" << endl;
}
A(const A& a)
{
cout << "A(const A & a)" << endl;
}
};
A f(A a)
{
A v(a);
A w = v;
return w;
}
int main()
{
A u;
A y = f(f(u));
return 0;
}
- 返回值出了函数体,不在了,就用传值返回;出了调用的函数体还在,就用引用返回.
前置++后置++
重载++,前置+=和后置++都是operator++(),所以需要在后置++时加上int
占位,构成函数重载.后置++要多两次拷贝构造,所以对于自定义类型推荐前置++.
帮助理解:前置++,返回的是自身加加之后的值,而且返回的时候自身对象还在,所以是引用返回.后置++是返回的原本的值,但是返回时还得完成++,所以需要拷贝构造一个自己并完成自己本身的加加,返回的是影子,因为影子的作用域仅限于这个函数体,所以是传值返回.
> < != == += + - -=
实现一种,然后根据逻辑关系实现其他时采取复用代码的方式.所有的类涉及到比较大小都可以采取这种复用的方式.
注意:+=,-=可能提供的是负数导致我们的逻辑出错,加上特判,如果是负数就去调用相反的函数即可.
-
日期相减计算相隔多少天
比较大小之后,小的日期++,加了多少次就是相隔多少天.
-
给定日期计算是星期几
1900年的1月1日是星期一,日期相减,然后取模7.
const成员
const Date*
不能传给Date*
类型,涉及到权限的放大,所以C++引入const成员函数,如果在函数Print()后面加了一个const 就可以了.传参的时候就是const Date* const this
成员函数加上const是好的,能加上就都加上,这样普通对象(权限的缩小)和const对象都可以调用。只要不改变都可以加上const,+=之类改变的不能加上.也可以起到保护的作用,如果有代码尝试修改,就会报错.
取地址运算符的重载,不声明也是可以直接用的是默认成员函数。
operator>>&&operator<<
流提取运算符的重载用于自定义类型的使用。
//如果>>重载为成员函数
class Date
{};
int main()
{
Date d(2022,1,1);
//cout<<d;//这样是跑不起来的
//操作符重载里面,如果是双操作数的操作符重载,第一个参数是左操作数,第二个是右操作数.
d.operator<<(cout);
d<<cout;//不符合使用习惯和使用含义
}
如果不是类里面的成员函数就可以定义成全局函数就可以正常使用,但是这样访问不了私有成员.
为了解决,就引用了友元的,friend设置,友元的声明要放到类的里面。这样处理将就能够访问内置成员变量.类的里面的成员函数默认是第一个是成员变量的this指针,所以放到全局里面使用全局函数,重载的是全局函数.全局函数就和this什么的没关系的.
#include<iostream>
using namespace std;
class Date
{
friend istream& operator>>(istream& in, Date& d);
friend ostream& operator<<(ostream& out,const Date& d);
public:
int GetMonthDay(int year,int month)
{
static int arr[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
int day = arr[month];
if (month == 2 && ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0)))
{
day += 1;
}
return day;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
cout << "拷贝构造函数" << endl;
}
Date(int year = 1, int month = 1, int day = 1)
{
_day = day;
_month = month;
_year = year;
cout << "构造函数" << endl;
}
bool operator>(const Date& d)
{
if (_year > d._year)
{
return true;
}
else if (_year == d._year && _month > d._month)
return true;
else if (_year == d._year && _month == d._month && _day > d._day)
return true;
else
return false;
}
Date operator-(int day)
{
Date tmp(*this);
tmp -= day;
return tmp;
}
Date& operator-=(int day)
{
if (day < 0)//客户脑子问题,减一个负数
{
return *this += -day;
}
_day -= day;
while (_day <= 0)
{
--_month;
if (_month == 0)
{
--_year;
_month = 12;
}
_day += GetMonthDay(_year,_month);
}
return *this;
}
bool operator==(const Date& d)
{
return (_year == d._year) && (_month == d._month) && (_day == d._day);
}
bool operator>=(const Date& d)
{
return *this > d || *this == d;
}
bool operator<(const Date& d)
{
return !(*this >= d);
}
bool operator!=(const Date& d)
{
return !operator==(d);
}
//赋值重载
Date& operator=(const Date& d)
{
if (*this != d)//this!=&d,对象比较->地址比较
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
//cout << "赋值重载" << endl;
}
Date& operator+=(int day)
{
if (day < 0)
{
return *this -= -day;
}
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month == 13)
{
_year++;
}
}
return *this;
}
Date operator+(int day)
{
Date tmp(*this);
tmp += day;
return tmp;
}
//++d
Date& operator++()
{
*this += 1;
return *this;
}
//d++ d1.opeartor(&d1,0)
Date operator++(int)
{
Date tmp(*this);
*this += 1;
return tmp;
}
//日期相减
int operator-(const Date& d)
{
Date max = *this;
Date min = d;
int flag = 1;
if (*this < d)
{
flag = -1;
max = d;
min = *this;
}
int count = 0;
while (max != min)
{
count++;
++min;
}
return count * flag;
}
void GetWeekDay(Date& d)
{
const char* arr[] = {"周一","周二","周3","周4","周5","周6","周日"};
Date start(1900,1,1);
int count = *this - start;
cout << arr[count] << endl;
}
private:
int _day;
int _month;
int _year;
};
istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
ostream& operator<<(ostream& out,const Date& d)
{
out << d._year << " " << d._month << " " << d._day << endl;
return out;
}
//int main()
//{
// Date d1(2022,1,3);
// //d1++;
// ++d1;
// //d1 += 60;
// /*d1 -= -69;
// cout << d1 << endl;*/
// /*Date ret1 = d1++;
// cout << ret1 << endl;
// Date ret2 = ++d1;
// cout<< ret2 << endl;*/
//
// //Date d1(2022,1,16);
// //Date d2(2023, 1, 7);
// //Date d4 = d2;
// //Date d5;
// //d5 = d4 = d1;//连续赋值,就需要赋值构造有返回值然后向前赋值操作
// //cout << d4 << endl;
// //cout << d5 << endl;
// /*Date d6 = d1;
// cout << d6 << endl;*/
// //cout << &d6 << " " << &d1 << endl;
// //cout << &d4 << " " << &d2 << endl;
// //d1 > d2;
// //int day = d2 - d1;
// /*Date d3;
// cin >> d3;
// cout << d3;*/
// return 0;
//}
再谈构造函数
初始化列表初始化
- 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
const常量初始化只有一次机会就是在定义的时候就赋值,而private下成员变量声明的地方是没有空间接收初始化的.一旦对象进入构造函数完成定义就无法再次赋值,所以引进了初始化列表->每个对象的成员变量定义的地方.
-
以下三个成员的变量必须是初始化列表初始化,他们都必须在定义的地方初始化
const,引用,没有默认构造函数的自定义类型成员变量
对于其他类型的成员变量,如:_year等在哪里初始化都可以,因为他们没有必须要求在定义的时候就初始化
class A
{
public:
A(int a)//非默认构造函数
{
_a = a;
}
private:
int _a;
};
class Date
{
public:
//初始化列表
Date(int y, int m, int d,int i)
:_year(y)
, _month(m)
, _day(d)
, _N(10)
,_ref(i)
,a(-1)//没有默认构造函数可用就在这里显示的调用
{}
private:
int _day;
int _year;
int _month;
const int _N;//声明
int& _ref;
A a;
};
int main()
{
//Date d1(2023,1,13);//对象定义
Date d1(2023,1,13,10);
return 0;
}
- 不使用初始化列表
-
使用初始化列表
所以,内置类型的成员,在函数体和初始化列表初始化都可以的,但是自定义类型推荐在初始化列表初始化
-
成员变量在类中的声明次序是其在初始化列表的初始化顺序,其实空间到这里都已经开好了.
explicit 禁止隐士类型转换
C语言中的隐式类型转换:相近类型-意义相似的类型
这个过程中会构造一个临时变量,在将这个临时变量拷贝构造给前面那个.
自定义类型支持单参数的构造函数,这样一个整形就可以创建一个临时对象C(10)
,在赋值给前面那个.
C++编译器在连续的一个过程中,多个构造会被优化,直接优化为一个构造就行,
- 所以为了避免隐士类型转换的发生引入explicit。在构造函数前面加上explicit.
引用临时变量具有常性,所以前面的要加上const。
static成员
类里面声明静态成员变量属于整个类属于所有对象,生命运行周期在整个程序运行期间.需要在类的外面初始化,和这个类深度绑定.如果不是静态的就和每个对象绑定就没意义了.
-
一个类到底创建了多少个对象?
采用静态成员变量进行计数器
class D
{
public:
D(int d)
{
_d = d;
++_scount;
}
D(const D& d)
{
_d = d._d;
++_scount;
}
//没有this指针,只能访问静态的成员变量和成员函数
static int GetCount()
{
return _scount;
}
private:
int _d;
static int _scount;//类里声明
};
int D::_scount = 0;//类外定义
int main()
{
D d1(10);
D d2(d1);
D d3 = 1;
//类外访问
//只能是公有的情况
/*cout << D::_scount << endl;
cout << d1._scount << endl;
cout << d2._scount << endl;*/
//私有的情况
cout << d1.GetCount() << endl;
cout << D::GetCount() << endl;
}
- 使用静态成员变量计算1+…+n的和
class Sum
{
public:
Sum()
{
ret+=i;
++i;
}
static int GetRet()
{
return ret;
}
private:
static int ret;
static int i;
};
int Sum::ret=0;
int Sum::i=1;
class Solution
{
int GetSum(int n)
{
Sum a[n];//支持变长数组
return Sum::GetRet();
}
};
static限制对象具有文件域,只能在当前.cpp文件所在的编译模块中使用。
静态成员函数没有this 指针,只能访问静态成员变量和成员函数 。
静态成员变量只能在类外面初始化
static成员函数只能调用静态变量和其他static函数
static成员变量在对象生成之前生成。
C++初始化成员新玩法-缺省值
因为默认构造函数对于自定义类型不处理,新玩法就是给的是成员变量缺省值,声明的时候给个缺省值.这里不是初始化而是缺省值.如果在初始化列表阶段没有对成员变量初始化,他就用缺省值初始化.
class F
{
public:
F(int f)
{
_f = f;
}
private:
int _f;
};
class E
{
public:
E()
{}
private:
int _e1 = 0;
F _f1 = 10;
F _f2 = F(20);
int* p = (int*)malloc(4*10);
int arr[10] = {1,2,3,4,5,6};
};
静态的成员变量不能给缺省值,必须在类外面初始化
友元
友元函数 和友元类:在一个普通函数当中访问对象的私有变量
突破封装访问私有private里面的成员,友元就像是黄牛一样,破坏了管理规则
友元是单向的,我是你的友元,我就可以访问你的。
友元函数不具备this指针,全局变量也不具备this指针
友元函数相当于全局函数可以直接调用
友元函数不可以继承和传递。
内部类
内部类:在一个类里面再定义一个类
内部类和在全局定义的类基本一样,只是他受外部类类域的限制
内部类天生就是外部类的友元,也就是B中可以访问A的私有.但是外部类不能访问内部类的私有.当在内部类添加友元之后外部类才可以访问.
this注意
- 基类保护成员在子类可以直接被访问,跟this无关
- 基类私有成员在子类中不能被访问,跟this无关
- 基类共有成员在子类和对象外都可以直接访问,跟this无关
玩法-缺省值
因为默认构造函数对于自定义类型不处理,新玩法就是给的是成员变量缺省值,声明的时候给个缺省值.这里不是初始化而是缺省值.如果在初始化列表阶段没有对成员变量初始化,他就用缺省值初始化.
class F
{
public:
F(int f)
{
_f = f;
}
private:
int _f;
};
class E
{
public:
E()
{}
private:
int _e1 = 0;
F _f1 = 10;
F _f2 = F(20);
int* p = (int*)malloc(4*10);
int arr[10] = {1,2,3,4,5,6};
};
静态的成员变量不能给缺省值,必须在类外面初始化
友元
友元函数 和友元类:在一个普通函数当中访问对象的私有变量
突破封装访问私有private里面的成员,友元就像是黄牛一样,破坏了管理规则
友元是单向的,我是你的友元,我就可以访问你的。
友元函数不具备this指针,全局变量也不具备this指针
友元函数相当于全局函数可以直接调用
友元函数不可以继承和传递。
内部类
内部类:在一个类里面再定义一个类
内部类和在全局定义的类基本一样,只是他受外部类类域的限制
内部类天生就是外部类的友元,也就是B中可以访问A的私有.但是外部类不能访问内部类的私有.当在内部类添加友元之后外部类才可以访问.
this注意
- 基类保护成员在子类可以直接被访问,跟this无关
- 基类私有成员在子类中不能被访问,跟this无关
- 基类共有成员在子类和对象外都可以直接访问,跟this无关
- this指针代表了当前对象,能够区分每个对象的自身数据.