C++类和对象补充

 更新时间:2021年10月22日 00:03  点击:1495 作者:蓝乐

一. 再看构造函数

我们之前已经了解了构造函数的基本内容,那么这里我们将深入认识构造函数。

1.函数体内赋初值

class Date
{
public:
 Date(int year, int month, int day)
 {
 _year = year;
 _month = month;
 _day = day;
 //可以进行多次赋值,但一般不这么做
 _year = 1;
 }
private:
 int _year;
 int _month;
 int _day;
};

首先,对于构造函数体内的赋值我们不能称之为初始化。首先我们要理解:初始化只能初始化一次,而构造函数体内可以多次赋值。那么对象成员变量的初始化是在什么时候进行的呢?这就要接下来要介绍的初始化列表要做的事了。

2.初始化列表

初始化列表是以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。其形式如下:

class Date
{
public:
	Date(int year = 0, int month = 1, int day = 1)
		:_year(year)
		,_month(month)
	{
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

几点注意

1.每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)

2.类中包含以下成员,必须放在初始化列表位置进行初始化:

(1)const成员变量:由于const变量初始化之后就不能更改,因此需在初始化列表进行初始化。

(2)引用成员变量:引用成员变量只能作为一个变量的引用,一旦初始化,就不能再作为其他变量的引用,因此引用变量也只能再初始化列表初始化。

(3)自定义类型成员变量(没有默认构造函数情况下):由于没有默认构造函数时,自定义类型变量是不能初始化的,此时程序也无法编译,因此没有默认构造函数的自定义类型成员变量必须在初始化列表进行初始化。

class B
{
public:
	B(int i)
		:_i(i)
	{
	}
private:
	int _i;
};
class A
{
public:
	A(int a, int& b, int bb)
		:_a(a)
		,_b(b)
		,_bb(bb)
	{
	}
private:
	const int _a;//const成员变量
	int& _b;//引用成员变量
	B _bb;//自定义成员变量
};

3.尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。比如下面代码的执行结果:

class B
{
public:
	B()
	{
		cout << "B()" << endl;
	}
private:
	int _i;
};
class A
{
public:
	A(int a, int& b)
		:_a(a)
		,_b(b)
	{
	}
private:
	const int _a;//const成员变量
	int& _b;//引用成员变量
	B _bb;//自定义成员变量
};
int main()
{
	int n = 0;
	A a1(0, n);
	return 0;
}

在这里插入图片描述

可以看到,初始化列表中并没有对自定义变量_bb初始化,但程序仍然调用了自定义类型的默认构造函数。

4. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关,先想想下面的代码运行结果是什么:

class A
{
public:
	A(int a)
		:_a1(a)
		, _a2(_a1)
	{}
	void Print() {
		cout << _a1 << " " << _a2 << endl;
	}
private:
	int _a2;
	int _a1;
};
int main() 
{
	A aa(1);
	aa.Print();
	return 0;
}

在这里插入图片描述

可以看到的是,_a1为1,而_a2为随机值,这是因为在成员列表的声明中,_a2先被声明,_a1后被声明,因此初始化列表中的顺序是先_a2,后_a1。而一开始_a1为随机值,因此最终_a2为随机值。

3.explicit关键字

我们知道,对于构造函数,不仅可以构造和初始化对象,对于单个参数的构造函数,还具有类型转换的作用。

比如Date类:

class Date
{
public:
	Date(int year)
		:_year(year)
	{}
	explicit Date(int year)
		:_year(year)
	{}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2020);
	// 用一个整形变量给日期类型对象赋值
	// 实际编译器背后会用2019构造一个无名对象,最后用无名对象给d1对象进行赋值
	Date d2 = 2021;//explict禁止隐式类型转换,因此该句代码运行错误
}

但是Date d2 = 2021;这样的代码可读性不是很好,因此可以使用explicit关键字将这种隐式类型转换禁止。

二.static成员

C语言中我们就接触了static关键字,那么这个关键字修饰成员会怎么样呢?

1.概念

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。像上面初始化列表中说的,静态的成员变量一定要在类外进行初始化。

2.特性

静态成员存储在静态区,为所有类对象所共享,不属于某个具体的实例

静态成员变量必须在类外定义,定义时不添加static关键字

类静态成员即可用类名::静态成员或者对象.静态成员来访问

静态成员函数没有隐藏的this指针,不能访问任何非静态成员;相对的,非静态成员函数可以通过this指针访问静态成员变量。

静态成员和类的普通成员一样,也有public、protected、private 3种访问级别,也可以具有返回值

接下来我们来看看一道题:

求1+2+3+…+n

题目描述:求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
这道题我们可以利用构造函数,由于每次实例化对象,都会调用其构造函数,因此我们可以实例化n个对象,每次初始化时计算求和即可;

class Sum
{
public:
    //调用构造函数
    Sum()
    {
        _sum += _i;
        ++_i;
    }
    //static修饰的成员函数,没有隐含的this指针,只能访问静态成员变量
    static int GetSum()
    {
        return _sum;
    }
private:
	//static修饰的成员变量为所有定义出来的类对象共有
    static int _i;
    static int _sum;
};
//静态成员变量的定义
int Sum::_i = 1;
int Sum::_sum = 0;
class Solution {
public:
    int Sum_Solution(int n) {
        Sum* p = new Sum[n];
        return Sum::GetSum();
    }
};

【注意】sizeof(类名)不计算静态成员变量的大小。比如上述代码中的sizeof(Sum)为1,是一个空类。

三.友元

友元分为友元函数和友元类,其提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。

1.友元函数

首先如果我们要重载<<(流插入)运算符,我们会发现将其定义成类成员函数将无法实现,这是因为类成员函数的第一个参数为this指针,那么我们只能将这个函数定义在类外,但是这样的话函数又不能访问类中的成员变量,那么这个时候要么在成员函数中实现访问的方法,要么就使用友元函数,使其可以访问类中成员。即:

class Date
{
	//用关键字friend在类中声明函数为Date的友元函数
	friend ostream& operator<<(ostream& out, const Date& d);
public:
	Date(int year, int month, int day)
		:_year(year)
		,_month(month)
		,_day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};
ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "/" << d._month << "/" << d._day;
	return out;
}
int main()
{
	Date d1(2021, 10, 20);
	cout << d1 << endl;
}

同理,cin也可以如此定义。

【说明】

1.友元函数可访问类的私有和保护成员,但不是类的成员函数

2.友元函数不能用const修饰

3.友元函数可以在类定义的任何地方声明,不受类访问限定符限制

4.一个函数可以是多个类的友元函数

5.友元函数的调用与普通函数的调用和原理相同

2.友元类

和友元函数相似,友元类可以访问另一个类的私有成员。比如下面代码中,B作为A的友元类,可以访问A中的_a和_i。

class A
{
	//声明B为A的友元类,则在B中可以访问A中的成员
	friend class B;
public:
	A(int a)
		:_a(a)
	{
	}
private:
	int _a;
	static int _i;
};
class B
{
public:
	B(int b)
		:_b(b)
	{}
	static int Count()
	{
		A::_i++;
		return A::_i;
	}
private:
	int _b;
};
int A::_i = 0;
int main()
{
	A a1(1);
	B b1(1);
	cout << b1.Count() << endl;
	cout << b1.Count() << endl;
	return 0;
}

需要注意,友元关系是单向的,不具有交换性,比如上述代码中A不能访问B中的成员;友元关系不能传递,即B是A的友元,C是B的友元,但C不是A的友元,C就不能访问A中的私有成员。

四.内部类

顾名思义,定义在另一个类中的类就是内部类。注意此时这个内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去调用内部类。外部类对内部类没有任何优越的访问权限。

内部类就是外部类的友元类。注意友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。

class A
{
public:
	class B//内部类,是A的友元类
	{
	public:
	//B可以直接访问A的成员
		void Print(const A& a)
		{
			cout << a._a << endl;
			cout << _i << endl;
		}
	};
	A(int a)
		:_a(a)
	{}
private:
	int _a;
	static int _i;
};
int main()
{
	A::B b1;//注意B的调用方式
	A a1(1);
	b1.Print(a1);
	//但A的对象不能去访问B中的成员
	a1.b1;//error
}

特性:

1.内部类可以定义在外部类的public、protected、private都是可以的。

2.注意内部类可以直接访问外部类中的static、枚举成员,不需要外部类的对象/类名。

3.sizeof(外部类)=外部类,和内部类没有任何关系。比如上面的sizeof(A)为4。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注猪先飞的更多内容!

原文出处:https://blog.csdn.net/love_guanghui/article/details/12087135

[!--infotagslink--]

相关文章

  • php svn操作类

    以前我们开发大型项目时都会用到svn来同步,因为开发产品的人过多,所以我们会利用软件来管理,今天发有一居然可以利用php来管理svn哦,好了看看吧。 代码如下 ...2016-11-25
  • C++ STL标准库std::vector的使用详解

    vector是表示可以改变大小的数组的序列容器,本文主要介绍了C++STL标准库std::vector的使用详解,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2022-03-06
  • java8如何用Stream查List对象某属性是否有重复

    这篇文章主要介绍了java8如何用Stream查List对象某属性是否有重复的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-11
  • PHP 数据库缓存Memcache操作类

    操作类就是把一些常用的一系列的数据库或相关操作写在一个类中,这样调用时我们只要调用类文件,如果要执行相关操作就直接调用类文件中的方法函数就可以实现了,下面整理了...2016-11-25
  • C++中取余运算的实现

    这篇文章主要介绍了C++中取余运算的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-23
  • 详解C++ string常用截取字符串方法

    这篇文章主要介绍了C++ string常用截取字符串方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
  • C++调用C#的DLL程序实现方法

    本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
  • R语言 如何删除指定变量或对象

    这篇文章主要介绍了R语言删除指定变量或对象的操作方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-05-06
  • C++中四种加密算法之AES源代码

    本篇文章主要介绍了C++中四种加密算法之AES源代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。...2020-04-25
  • JS+CSS实现分类动态选择及移动功能效果代码

    本文实例讲述了JS+CSS实现分类动态选择及移动功能效果代码。分享给大家供大家参考,具体如下:这是一个类似选项卡功能的选择插件,与普通的TAb区别是加入了动画效果,多用于商品类网站,用作商品分类功能,不过其它网站也可以用,...2015-10-21
  • C++ 整数拆分方法详解

    整数拆分,指把一个整数分解成若干个整数的和。本文重点给大家介绍C++ 整数拆分方法详解,非常不错,感兴趣的朋友一起学习吧...2020-04-25
  • Php文件上传类class.upload.php用法示例

    本文章来人大家介绍一个php文件上传类的使用方法,期望此实例对各位php入门者会有不小帮助哦。 简介 Class.upload.php是用于管理上传文件的php文件上传类, 它可以帮...2016-11-25
  • JavaScript预解析,对象详解

    这篇文章主要介绍了JavaScript预解析,对象的的相关资料,小编觉得这篇文章写的还不错,需要的朋友可以参考下,希望能够给你带来帮助...2021-11-10
  • C++中 Sort函数详细解析

    这篇文章主要介绍了C++中Sort函数详细解析,sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变...2022-08-18
  • 如何在Spring WebFlux的任何地方获取Request对象

    这篇文章主要介绍了如何在Spring WebFlux的任何地方获取Request对象,帮助大家更好的理解和使用springboot框架,感兴趣的朋友可以了解下...2021-01-26
  • C++万能库头文件在vs中的安装步骤(图文)

    这篇文章主要介绍了C++万能库头文件在vs中的安装步骤(图文),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-23
  • 牛叉的Jquery――Jquery与DOM对象的互相转换及DOM的三种操作

    只有jQuery对象才能调用jQuery类库的各种函数,同样有些dom对象的属性和方法在jQuery上也是无法调用的,不过基本上jQuery类库提供的函数包含了所有的dom操作。这就需要我们知道如何将jQuery对象和DOM的相互转换的方法。1...2015-10-30
  • PHP实现无限级分类(不使用递归)

    无限级分类在开发中经常使用,例如:部门结构、文章分类。无限级分类的难点在于“输出”和“查询”,例如 将文章分类输出为<ul>列表形式; 查找分类A下面所有分类包含的文章。1.实现原理 几种常见的实现方法,各有利弊。其中...2015-10-23
  • PHP实现递归无限级分类

    在一些复杂的系统中,要求对信息栏目进行无限级的分类,以增强系统的灵活性。那么PHP是如何实现无限级分类的呢?我们在本文中使用递归算法并结合mysql数据表实现无限级分类。 递归,简单的说就是一段程序代码的重复调用,当把...2015-10-23
  • 详解C++ bitset用法

    这篇文章主要介绍了C++ bitset用法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25