TIPS
- 当某一个类当中有自定义类型成员变量的时候,然后对该类的实例化对象调用函数的时候走初始化列表的时候,如果说要对自定义类型成员变量进行初始化列表初始化的时候,尽管那个自定义类型它的构造函数是没有参数的,但是此时括号是不能够省略掉的
class A
{
public:
A()
:a1(0)
,b1(0)
,c1(0)
{
}
private:
int a1;
int b1;
int c1;
};
class B
{
public:
B()
: a2(0)
, b2(0)
, c2(0)
, aa() //注意
{
}
private:
int a2;
int b2;
int c2;
A aa;
};
- 在函数重载当中去判断参数的类型的时候,是否被const修饰也是会影响类型的,尽管他们可能是同一个数据类型。
- 在我们之前也讲过,两个函数尽管构成了函数重载,但是实际上还有可能会发生对于重载函数调用不明确的问题,这个问题出现的原因主要在于有时候尽管两个函数已经是构成了函数重载,但是你实际在传参的时候,你会发现这两个函数它都是可以去匹配实参的,所以说就会发生调用不明确的问题
- 如果说一个函数它是返回实例化对象的时候,如果说他是传值返回,那么就会发生拷贝构造,但如果说他是引用返回的话,就不存在这个问题,所以说函数的引用返回的话它的效率是要更高一点
友元函数
- 友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字
- 代码样本:
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
class Stu
{
friend ostream& operator<<(ostream& out, Stu& s);
public:
//构造函数
Stu(int size = 10)
:_namesize(size)
,_age(0)
, _score(0)
{
_name = (char*)malloc(sizeof(char) * _namesize);
if (_name == nullptr)
{
perror("malloc failed");
return;
}
}
//拷贝构造函数
Stu(const Stu& s)
{
_namesize = s._namesize;
_age = s._age;
_score = s._score;
_name = (char*)malloc(sizeof(char) * _namesize);
if (_name == nullptr)
{
perror("malloc failed");
return;
}
memcpy(_name, s._name, sizeof(char) * (_namesize + 5));
}
//赋值运算符重载
Stu& operator=(const Stu& s)
{
_namesize = s._namesize;
_age = s._age;
_score = s._score;
_name = (char*)malloc(sizeof(char) * (_namesize + 5));
if (_name == nullptr)
{
perror("malloc failed");
exit(-1);
}
memcpy(_name, s._name, sizeof(char) * (_namesize + 5));
return *this;
}
//输入信息
void InputInfo()
{
cout << "依次输入学生的姓名,年龄与成绩";
cin >> _name >> _age >> _score;
}
//析构函数
~Stu()
{
free(_name);
}
//获取学生姓名
char* GetName()const
{
return _name;
}
//获取学生年龄
int GetAge()const
{
return _age;
}
//获取学生成绩
int GetScore()const
{
return _score;
}
private:
char* _name;
int _namesize;
int _age;
int _score;
};
//输出信息1
ostream& operator<<(ostream& out, Stu& s)
{
cout << s.GetName() << " " << s.GetAge() << " " << s.GetScore() << endl;
return out;
}
int main()
{
Stu s1(20);
s1.InputInfo();
Stu s2 = s1;
s1.InputInfo();
cout << s1;
cout << s2;
return 0;
}
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
class Stu
{
friend ostream& operator<<(ostream& out, Stu& s);
public:
//构造函数
Stu(int size = 10)
:_namesize(size)
,_age(0)
, _score(0)
{
_name = (char*)malloc(sizeof(char) * _namesize);
if (_name == nullptr)
{
perror("malloc failed");
return;
}
}
//拷贝构造函数
Stu(const Stu& s)
{
_namesize = s._namesize;
_age = s._age;
_score = s._score;
_name = (char*)malloc(sizeof(char) * _namesize);
if (_name == nullptr)
{
perror("malloc failed");
return;
}
memcpy(_name, s._name, sizeof(char) * (_namesize + 5));
}
//赋值运算符重载
Stu& operator=(const Stu& s)
{
_namesize = s._namesize;
_age = s._age;
_score = s._score;
_name = (char*)malloc(sizeof(char) * (_namesize + 5));
if (_name == nullptr)
{
perror("malloc failed");
exit(-1);
}
memcpy(_name, s._name, sizeof(char) * (_namesize + 5));
return *this;
}
//输入信息
void InputInfo()
{
cout << "依次输入学生的姓名,年龄与成绩";
cin >> _name >> _age >> _score;
}
//析构函数
~Stu()
{
free(_name);
}
private:
char* _name;
int _namesize;
int _age;
int _score;
};
//输出信息2
ostream& operator<<(ostream& out, Stu& s)
{
cout << s._name << " " << s._age << " " << s._score << endl;
return out;
}
int main()
{
Stu s1(20);
s1.InputInfo();
Stu s2 = s1;
s1.InputInfo();
cout << s1;
cout << s2;
return 0;
}
友元类
- 友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
- 友元关系是单向的,不具有交换性。
- 就是我的类成为你的类的朋友,那么在我的类当中可以随便去访问你的私有成员
- 代码样本
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
class Time
{
friend class date;
public:
Time()
:_hours(0)
,_minutes(0)
,_seconds(0)
{
}
private:
int _hours;
int _minutes;
int _seconds;
};
class date
{
public:
date()
: _years(2000)
, _months(1)
, _days(1)
, _t()
{
}
void Show()
{
cout << _years << "年" << _months << "月" << _days << _t._hours << "时" << _t._minutes << "分" << _t._seconds << "秒" << endl;
}
private:
int _years;
int _months;
int _days;
Time _t;
};
int main()
{
date d;
d.Show();
return 0;
}
内部类
- 一般来说类都是定义在全局当中,但也有情况就会发生:类B居然定义在某个其他类A的类域当中,这个就是内部类。但除非它B创建了一个实例化对象,否则的话是完全没有占任何空间,因为B那仅仅是一个声明而已,根本就不会占任何空间(创建实例化对象除外)。现在假设要在main函数里面去创建一个类B的实例化对象,不能直接这么去创建,需要用域作用限定符去指定一下命名搜索顺序,即:A::B b1; 这样子。
注:在用sizeof计算类的实例化对象的大小的时候,对于静态成员变量是不需要计算进去的,因为静态成员变量它并不是存在类的实例化对象当中,它是存在内存的静态区当中了 - 然后这个内部类,比如说上面这个类B,如果说他是定义在类A的私有区域当中,那么这样子的话,在类A的外面是怎么样都没有办法去访问的,因此内部类的话是受到访问限定符的限制的。
- 内部类是外部类的天生友元,注意并不是互相友元,是对于内部类你可以想象成小棉袄一样,有特殊优惠。于此同时,需要认识到:友元类之间涉及到的是类的私有成员变量的访问限制问题
- 并且在某个类当中,我们知道相当于是不受访问限定符的限制,各个平级关系的话,都是可以互相之间进行访问,但如果并不是平级的,就说相当于有代际差异(比如说内部类), 那这时候又需要另外去考虑
内部类使用的例子
luck
class Solution
{
//内部类
class Sum
{
public:
Sum()
{
s+=i;
i++;
}
};
public:
int Sum_Solution(int n)
{
Sum arr[n];
return s;
}
private:
static int i;
static int s;
};
int Solution::i=1;
int Solution::s=0;
关于友元类,友元函数与外部类的关系
匿名对象
- 匿名对象的生命周期只有在那一行,也就是说是即用即销毁。
- 他与普通对象都是一样往构造函数当中去传参,只不过是没有名字而已,并且构造函数内部如果没有参数可以传的话,它的括号不能够省略
class A
{
public:
A(int a, int b)
:_a(a)
,_b(b)
{
}
A()
{
}
private:
int _a = 1;
int _b = 1;
};
int main()
{
A(1, 3);
A();
return 0;
}
- 与之前讲的临时变量一样,匿名对象具有常性;如果说对匿名对象进行普通的引用的话,那么相当于权限会放大,那么就是不行的。如果是进行const 引用,那么相当于是权限的平移,那是可以进行引用的。并且一旦匿名对象被引用了之后,他可以延长匿名对象的生命周期,生命周期延申到当前函数的局部域。强行续命
- 并且匿名对象没有个数限制,你想创建多少个就可以创建多少个
再次理解类和对象
现实生活中的实体计算机并不认识,计算机只认识二进制格式的数据。如果想要让计算机认识现实生活中的实体,用户必须通过某种面向对象的语言,对实体进行描述,然后通过编写程序,创建对象后计算机才可以认识。比如想要让计算机认识洗衣机,就需要:
- 用户先要对现实中洗衣机实体进行抽象—即在人为思想层面对洗衣机进行认识,洗衣机有什么属性,有那些功能,即对洗衣机进行抽象认知的一个过程
- 经过1之后,在人的头脑中已经对洗衣机有了一个清醒的认识,只不过此时计算机还不清楚,想要让计算机识别人想象中的洗衣机,就需要人通过某种面相对象的语言(比如:C++、Java、Python等)将洗衣机用类来进行描述,并输入到计算机中
- 经过2之后,在计算机中就有了一个洗衣机类,但是洗衣机类只是站在计算机的角度对洗衣机对象进行描述的,通过洗衣机类,可以实例化出一个个具体的洗衣机对象,此时计算机才能洗衣机是什么东西。
- 用户就可以借助计算机中洗衣机对象,来模拟现实中的洗衣机实体了。在类和对象阶段,大家一定要体会到,类是对某一类实体(对象)来进行描述的,描述该对象具有那些属性,那些方法,描述完成后就形成了一种新的自定义类型,才用该自定义类型就可以实例化具体的对象。