目录

0.前言

1.构造函数的初始化问题

构造函数应该在什么地方初始化呢?

2.explicit关键字

3.static成员

4.友元

友元函数

友元类

5.内部类

6.匿名对象


0.前言

笔者我系统性的介绍了C++中类和对象的知识,本篇文章为《类和对象(下)》,主要介绍了一下构造函数的初始化问题、explicit关键字、static成员、友元、内部类、匿名对象。如果你想系统的了解C++中类和对象的知识,推荐阅读下面两篇文章。

类和对象(上)icon-default.png?t=O83Ahttps://blog.csdn.net/D5486789_/article/details/143301530?spm=1001.2014.3001.5501

类和对象(中)icon-default.png?t=O83Ahttps://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;
}
  • 需要注意的是,匿名对象的生命周期只在这一行。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部