- 引用
- this指针
- 构造函数
- 析构函数
- 运算符重载
- 友元函数
一,引用
在C语言中,我们了解到函数传递参数的方式有传值和传址。
传值
优点:函数的副作用不会影响到外部的实参
缺点:不能通过修改参数来改变外部实参
————————————————————————
传址
优点:节省空间,效率高,改变参数可以改变外部实参
缺点:指针不安全
在前面两种传参的方式下,C++又给我们提供了传引用的方式。
引用:引用不是新定义了一个变量,而是给已经存在的变量取了一个别名,编译器不会为引用开辟新的空间,它与它引用的变量共用一块内存空间。
int main()
{
int a = 10;
int& aa = a; //对a引用
printf("%d\n",aa);
aa++;
printf("%d\n",a);
return 0;
}
引用的特性:
1.引用在定义的时候必须初始化
2.一个变量可以有多个引用,但一个引用变量只能对一个实体引用
常引用及数组引用:
如果一个常量被const修饰,那么引用也必须用const修饰。
如:
const int a = 10;
const int& aa = a;
int b[20]; //数组引用
int &bb[20] = b;
ps:使用引用传递参数,是否会生成临时变量?
答:会生成临时变量。
传引用和传指针的区别?
相同点:
- 底层的实现方式都一样,都是按照指针的方式来实现的。
不同点:
- 引用在定义时必须初始化,指针没有要求(为了避免麻烦,也应养成习惯对指针初始化)
- 引用在初始化时只能引用一个实体,而指针可以任何时候指向任何一个同类型对象
- 没有NULL引用,但有NULL指针
- 在sizeof中,引用是引用类型的大小,指针始终是地址空间所占字节数
- 引用自加改变变量内容,指针自加改变指针指向
- 有多级指针,没有多级引用
- 指针需要手动寻地址,引用通过编译器实现寻址
- 引用比指针使用起来安全和便捷
二,this指针
this指针是指向实例化对象本身的一个指针,里面存储的是对象本身的地址,可以通过该地址访问该对象的成员函数和成员变量。
- this指针的类型:类类型* const
- this指针不是对象本身的一部分,不会影响sizeof的结果
- this指针是“类成员函数”的第一个默认参数,编译器会自动维护传递,类编写者不能显式定义
- this指针代表着当前的对象,只有在类的非静态成员函数才可以使用this指针,其他任何函数都不可以.
三,构造函数
构造函数:是一个特殊的成员函数,名字和类名相同,创建类类型对象时,由编译器自动调用,在对象的生命周期里,只调用一次,保证每个类成员变量都有一个初始值。
class Date
{
public:
Date(int year = 0,int month = 0,int day = 0)
:_year(year)
,_month(month)
,_day(day)
{
}
private:
int _year;
int _month;
int _day;
};
以上是一个日期类的缺省构造函数。
构造函数特性:
- 函数名与类名相同
- 没有返回值
- 新对象构建时,系统会自动调用
- 构造函数可以重载,实参决定了调用那个构造函数
- 无参构造函数和带有缺省值得构造函数都认为是缺省的构造函数,并且缺省的构造函数只能有一个
- 如果在类中,我们没有给出显式定义的构造函数的时候,编译器会自动合成一个
- 构造函数不能用const修饰
- 构造函数不能为虚函数
ps:类中包含以下成员时,一定要在初始化列表位置进行初始化
引用成员变量
const成员变量
类类型成员(该类有非缺省的构造函数)
编译器会在什么情况下,自动合成构造函数?
第一种情况:A类没有构造函数,B类中构造函数,A中有B类对象作为私有成员,这时生成A类对象时,编译器会合成构造函数,并在初始化成员的时候,调用B类定义的构造函数
四,拷贝构造函数
拷贝构造函数:只有单个参数,而且该形参是对本类类型对象的引用(常用const修饰),创建对象时,使用已经存在的同类对象来初始化,由编译器自动调用
class Date
{
public:
Date(int year = 0,int month = 0,int day = 0)
:_year(year)
,_month(month)
,_day(day)
{
}
Date(const Date& d)
:_year(d._year)
,_month(d._month)
,_day(d._day)
{
}
private:
int _year;
int _month;
int _day;
};
- 构造函数的特性,拷贝构造函数均满足
- 如果类中没有显式定义,系统会自动合成一个默认的拷贝构造函数,默认的拷贝构造会依次拷贝类的数据成员完成初始化
- 参数必须使用类类型对象的引用传递
ps:参数为什么必须使用类类型对象的引用传递?
如果我们传递的不是引用,那么传递的是一个类对象,由于参数并没有初始化,所以会调用构造函数生成参数的一个对象对参数进行赋值,对参数进行赋值就会调用拷贝构造,而拷贝构造还是会调用构造函数对参数进行初始化。这样会一直递归下去,直到程序崩溃。
四,析构函数
析构函数:与构造函数功能相反,在对象被销毁时,由编译器自动调用,完成类资源的清理。
class Arrary
{
public:
Arrary()
{
_array = new int[10];
}
~Array()
{
if(_array)
delete _array;
}
private:
int *_array;
}
析构函数的特性:
- 析构函数在类名加上字符 ~
- 析构函数无参数和返回值
- 一个类只有一个析构函数,如果没有显式定义,系统会自动合成缺省的析构函数
- 对象生命周期结束,编译器会自动调用析构函数
- 析构函数并不是删除对象,只是做一些清理工作
五,运算符重载
运算符重载是具有特殊函数名的函数,关键字operator后面接需要定义的操作符符号。操作符重载是一个函数,有返回值和参数,它的参数数目与操作符的操作数目一样。
不可以重载的运算符:
成员选择符 ‘ . ’
成员对象选择符 ‘ .* ’
域解析操作符 ‘ :: ’
条件操作符 ‘ ?: ’
class String
{
public:
String(const char* str = "")
{
if (NULL == str)
{
_str = new char[1];
_str = '\0';
}
else
{
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
}
String& operator=(const String& s) //运算符重载,是一个函数,有返回值,有参数。参数为2个
{ //一个为隐藏的this指针一个为const对象的引用
if (&s != this)
{
if (_str)
delete _str;
_str = new char[strlen(s._str) + 1];
strcpy(_str, s._str);
}
return *this;
}
private:
char* _str;
};
int main()
{
String s1("123");
String s2;
s2 = s1; //运算符的操作数目为2,重载的参数也是2
return 0;
}
- 不能连接其他符号创建新的操作符,这样做没意义
- 一般将算术操作符定义为非成员函数,将赋值运算符定义为成员函数
- == 和 != 操作符一般要成对重载
- 下标操作符 [ ] :一个非const成员并返回引用,一个const成员并返回引用
- 输入操作符>>和输出操作符<<必须定义为类的友元函数
六,友元函数
如果我们想访问一个类的私有成员时,我们需要经过类内的成员函数去访问。C++中还为我们提供了另一种方式。那就是友元函数。
友元函数:友元函数可以直接访问类内的私有成员,它是定义在类外的普通函数,不属于任何类,但需要在类内声明,声明时需要加friend关键字。
class Date
{
friend void print(const Date& d); //声明,可以在类内的任何地方
public:
Date(int year = 0,int month = 0,int day = 0)
:_year(year)
,_month(month)
,_day(day)
{
}
private:
int _year;
int _month;
int _day;
};
void print(const Date& d)
{
cout<<d._year<<"."<<d._month<<"."<<d._day<<endl;
}
友元类
友元类的所有成员函数都可以是另一个的友元函数,都可以访问另一个类的私有成员。
缺点:破坏了类的封装和隐藏性
优点:提高了程序的运行效率
友元关系不能继承
友元关系是单向的,不具有交换性
友元关系不能传递