目录
0.前言
笔者我系统性的介绍了C++中类和对象的知识,本篇文章为《类和对象(下)》,主要介绍了一下构造函数的初始化问题、explicit关键字、static成员、友元、内部类、匿名对象。如果你想系统的了解C++中类和对象的知识,推荐阅读下面两篇文章。
类和对象(上)https://blog.csdn.net/D5486789_/article/details/143301530?spm=1001.2014.3001.5501
类和对象(中)https://blog.csdn.net/D5486789_/article/details/143341125?spm=1001.2014.3001.5501
1.构造函数的初始化问题
我们之前写的构造函数其实并没有初始化,顶多成为赋初值,因为初始化只能初始化一次,但是函数体内可以多次赋值,所以,我们之前的构造函数并没有能够初始化成员变量的地方。
class cube
{
public:
cube(int l, int w, int h)
{
//函数体内赋初值
_length = l;
_width = w;
_height = h;
}
private:
// 声明
int _length;
int _width;
int _height;
};
所以,我们现在要进一步探究一下构造函数。
构造函数应该在什么地方初始化呢?
在我们定义对象的时候,这个时候是对象整体定义,开辟对象空间,需要调用构造函数完成对象中成员变量的初始化,而构造函数会在初始化列表中完成对象的初始化。
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个成员变量后面跟一个放在括号中的初始值或表达式。
- 每个值只能在初始化列表中出现一次。(否则就不叫初始化了)
- 引用成员变量、const成员变量、没有默认构造函数的自定义类型成员,这些成员都必须在定义的时候初始化,所以,必须在初始化列表位置进行初始化。
- 没有在初始化列表显示初始化的成员也会定义。内置类型成员给随机值,相当于不处理;自定义类型成员会去调用它的默认构造函数。
使用初始化列表初始化对象如下:
注意:
- 类中的成员变量是声明,不是定义。
- 初始化列表中成员变量的初始化顺序和声明的顺序一致,和初始化列表中的顺序无关。(为了好理解程序,建议初始化的顺序和声明的顺序保持一致)
2.explicit关键字
构造函数还有一个隐藏功能 —— 传递单个参数的构造函数支持内置类型隐式转换成自定义类型。
具体过程如下图:当我们使用1给cube类型的对象c赋值时,此时会调用 “传递单个参数的构造函数” 构造出一个临时变量,然后调用拷贝构造函数,用这个临时变量拷贝构造出对象c。
如果不想让这种隐式类型转换发生,可以在构造函数前面添加 explicit关键字,表示禁止这种隐式类型转换发生。
如下所示:
3.static成员
用static修饰的类成员叫做类的静态成员,其中,用static修饰的成员变量叫做静态成员变量,用static修饰的成员函数叫做静态成员函数。
class cube
{
public:
// 静态成员函数
static int get_count()
{
return _count;
}
private:
int _length;
int _width;
int _height;
static int _count; // 声明静态成员变量
};
// 静态成员变量必须在类外初始化
int cube::_count = 0;
静态成员的特性:
1、静态成员存放在静态区,所以不属于某一个对象,而是属于所有的类对象。
2、静态成员变量必须在类外定义和初始化,定义时不添加static关键字,类中只是声明。
- 因为静态成员变量不是属于某一个类对象的,就不会在构造函数的初始化列表初始化静态成员变量,如果需要初始化的话,只能在类外通过指定类域的方式初始化。
3、静态成员有三种访问方式,分别是:
- 类名::静态成员
- 类对象.静态成员
- 类类型的指针变量->静态成员
示例如下:
int main()
{
cube c1(3,4,5);
cube* p = &c1;
// 类名::静态成员
cout << cube::get_count() << endl;
// 类对象.静态成员
cout << c1.get_count() << endl;
// 类类型的指针变量->静态成员
cout << p->get_count() << endl;
return 0;
}
4、静态成员函数没有隐藏的this指针,不能访问任何非静态成员。
- 非静态成员只能通过对象or指向对象的指针来访问,但是,静态成员函数不属于任何对象,所以没有this指针,也就无法直接访问非静态成员了。
5、静态成员也是类的成员,也受访问限定符的限制。
- 静态成员函数和静态成员变量的本质是 受 类域和访问限定符 限制的 全局函数 和 全局变量。
两个问题:
1、静态成员函数可以调用非静态成员函数吗?
- 不可以,非静态成员函数有隐藏的this指针,静态成员函数没有,参数不满足,无法调用。
- 同样,也不能直接访问非静态的成员变量。
2、非静态成员函数可以调用类的静态成员函数吗?
- 可以,静态成员函数属于所有类对象,可以直接访问。
- 同样,也可以访问静态成员变量。
4.友元
友元分为友元函数和友元类,我将依次介绍。
友元函数
我们都知道,一个类里面的私有成员只能在类内访问,类外的函数是无法访问的,但是如果这个函数是这个类的友元函数的话,他是可以直接使用这个类里面的私有成员的。
下面这份代码时无法通过编译的:
#include <iostream>
using namespace std;
class Test
{
public:
Test() {}
~Test() {}
private:
int _num1 = 1;
int _num2 = 2;
int _num3 = 3;
};
void print(const Test& t)
{
cout << t._num1 << endl;
cout << t._num2 << endl;
cout << t._num3 << endl;
}
int main()
{
Test t;
print(t);
return 0;
}
// 运行结果:无法通过编译
运行结果:
此时我们可以将print函数改为Test类的友元函数,只需要在类内中添加一句 “friend+函数声明” 即可。
代码如下:
#include <iostream>
using namespace std;
class Test
{
// 声明print函数是Test类的友元函数
friend void print(const Test& t);
public:
Test() {}
~Test() {}
private:
int _num1 = 1;
int _num2 = 2;
int _num3 = 3;
};
void print(const Test& t)
{
cout << t._num1 << endl;
cout << t._num2 << endl;
cout << t._num3 << endl;
}
int main()
{
Test t;
print(t);
return 0;
}
运行结果:
1
2
3
友元函数总结:
- 友元函数是定义在类外部的普通函数,不属于任何类。
- 友元函数可以直接访问类的私有成员。
- 友元函数需要在类内部声明,声明时要加friend关键字。
需要注意的是:
- 友元函数不能用const修饰,因为友元函数是普通函数,没有this指针。
- 友元函数可以在类内任何地方声明,不受访问限定符的限制。
友元类
如果我们有两个类,在一个类中声明一个类是另一个类的友元类,那么在这个友元类中就可以直接访问另一个类的私有成员了。
如下代码所示,我们有一个Time类和一个Date类,在Time类中声明Date类为它的友元类,那么在Date类中就可以直接访问Time类的私有成员了:
class Time
{
// 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量
friend class Date;
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 SetTime(int hour, int minute, int second)
{
// 直接访问时间类私有的成员变量
_t._hour = hour;
_t._minute = minute;
_t._second = second;
}
private:
int _year;
int _month;
int _day;
Time _t;
};
需要注意的是:
- 友元关系是单向的,不具有交换性。就好比上述代码中,Date类是Time类的友元类,Date类可以直接使用Time类的东西;但是Time类并不是Date类的友元类,Time类不能直接使用Date类的东西。
- 友元关系不能传递。比如:A是B的友元,B是C的友元,但是A不一定是C的友元。
- 友元关系不能继承。
5.内部类
如果一个类定义在另一个类的内部,这个类就叫做内部类。
下面代码中B是A的内部类,A是B的外部类:
class A
{
private:
static int _flag;
int _a;
public:
class B // B是A的内部类,B天生就是A的友元
{
public:
void test(const A& a)
{
cout << _flag << endl; //OK
cout << a._a << endl; //OK
}
};
};
内部类和外部类的关系:
- 内部类是外部类的友元,内部类可以通过外部类的对象参数来访问外部类的所有成员。
- 但是,外部类不是内部类的友元,内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员,外部类对内部类没有任何优越的访问权限。
注意事项:
- 内部类受到外部类的类域和访问限定符的限制。
- 内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
- sizeof(外部类)=外部类,和内部类没有任何关系。以上面代码为例,A类型的大小只计算了int类型的_a的大小。
6.匿名对象
我们在定义对象的时候,是不是常常为了取名字而头疼,C++祖师爷也同样如此,于是引入了匿名对象。
匿名对象的定义方式如下:类型名+()。
class A
{
public:
A(){}
~A(){}
private:
int _a;
};
int main()
{
// 定义匿名对象
A();
return 0;
}
- 需要注意的是,匿名对象的生命周期只在这一行。
本站资源均来自互联网,仅供研究学习,禁止违法使用和商用,产生法律纠纷本站概不负责!如果侵犯了您的权益请与我们联系!
转载请注明出处: 免费源码网-免费的源码资源网站 » 类和对象(下)
发表评论 取消回复