学习->C++篇三:类的构造函数与其它成员

目录

1.构造函数再探

(1)初始化和赋值

(2)初始化列表和初始化顺序

初始化顺序

(3)隐式类类型转换和explicit关键字

2.static成员

(1)静态成员变量

(2)静态成员函数

3.友元和内部类

(1)友元函数

(2)友元类

(3)内部类


1.构造函数再探

(1)初始化和赋值

        初始化和赋值是不同的概念,当我们在定义变量时,可以先定义再赋值,或者定义并初始化,事实上先定义不初始化,编译器就会默认初始化变量,接下来再对变量的赋值操作不能称为初始化而是赋新值。显然,定义时并初始化的效率比先定义再赋值的更高,在类的构造函数中也是如此。

    int a;//定义
    a = 100;//赋值
    int b = 200;//定义并初始化

(2)初始化列表和初始化顺序

        在类中定义了构造函数,函数体内的为成员变量赋值的操作不是初始化。因为编译器在构造函数体执行之前就执行了默认初始化。例如:

 但是有些成员必须在定义时就初始化它,比如引用,const或者没有默认构造函数的类类型成员。这样就不能在构造函数的函数体内进行初始化了。C++提供了唯一的给这些变量初始化并赋值的方式,这就是初始化列表。在构造函数的()后,{}之前写,格式是冒号+成员名(初始值),对与自定义类型则是调用它的构造函数初始化。

例如:

初始化顺序

        成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。

例如:

在初始化列表中m_b在m_a前,但是m_a声明在m_b前,故先用m_b的默认初始化值初始化m_a。

(3)隐式类类型转换和explicit关键字

首先看下面例子,用100构造了对象aa。

         可以这么理解,编译器用100构造了一个临时对象,再将这个临时对象拷贝构造aa,也就是将100隐式类型转换成了A类类型。对于这样连续的构造和拷贝构造的调用,编译器用寄存器优化了,只调用了一次构造函数。        

        转换规则也有限制,编译器只能支持一步的隐式类类型转换,例如:若要将”abc“转换成A类类型,就要先转换成string类型,再转换成A类型,进行两步的类类型转换,编译器不支持。可以显式类型转换来调用。

例如:

若类有能通过一个实参调用构造函数,则默认支持这个规则。要不让隐式类型转换发生,就要在类的对应构造函数前加上explicit关键字。

例如:

2.static成员

        在类中,被static修饰的成员是类的静态成员,它们虽能被该类实例化的对象访问,但是它们是该类所有对象共有的成员。类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问。静态成员也是类的成员,受访问限定符的限制。

(1)静态成员变量

        静态成员变量储存在静态区(数据段),用sizeof求类的大小时不计算在内,静态成员变量在类中只是声明,须在类外初始化,初始化时不加static关键字,但要加上类域(类名::)。类似于全局变量,静态成员变量只不过受了类域的限制,只能定义一次,生命周期直到程序结束。

(2)静态成员函数

        因为静态成员函数没有隐藏的this形参,故不能访问非静态成员变量,也不能直接调用非静态成员函数,也不能是const成员函数,非静态成员函数则可以直接调用静态成员函数。

例如:用静态成员函数和静态成员变量计算类实例化了几个对象

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;

class A
{
public:
	A(){static_var++;}
	static void countObject()
	{
		cout << "There are " << static_var << " A Objects"<<endl;
	}
	static int static_var;//在类内声明
};
int A::static_var = 0;//必须在类外初始化,不带static关键字
int main()
{
	A a1,a2,a3,a4,a5;
	A::countObject();//直接用类名访问
	A array[100];
	A::countObject();
	return 0;
}

输出:

3.友元和内部类

友元破坏了类的封装性,但有时提供了便利。友元会增加耦合度,破坏封装,故友元不宜多用。

(1)友元函数

例如:要重载运算符<<,因为this占用了第一个形参位置,所以不能重载成成员函数,但是重载成全局函数访问不了类的私有成员,这时可以用友元解决,将全局函数在类中声明为友元函数即可访问私有成员。

注:

1.友元函数是定义在类外部的普通函数,不属于该类。

2.需要在类的内部声明,声明时需要加friend关键字。

3.友元函数不能用const修饰(非成员函数)。
3.友元函数可以在类定义的任何地方声明,不受类访问限定符限制。
4.一个函数可以是多个类的友元函数。
5.友元函数的调用与普通函数的调用原理相同。
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class A
{
	friend ostream& operator<<(ostream& out, A& a);
public:
	A(int a = 1, int b = 2,int c=3)
		:m_a(a),m_b(b),m_c(c)
	{}
private:
	int m_a;
	int m_b;
	int m_c;
};
ostream& operator<<(ostream& out, A& a)
{
	out << a.m_a << "--"<<a.m_b << "--"<<a.m_c ;
	return out;
}
int main()
{
	A a;
    A b(100,200,300);
	cout << a << endl;
    cout << b << endl;
	return 0;
}

输出:

(2)友元类

将一个类声明为另一个类的友元,就是友元类,友元类的所有成员函数是另一个类的友元函数,都可以访问另一个类中的成员。

特性:

1.单向性,A是B的友元类,不代表B是A的友元类。
2.友元关系不能传递,如果A是B的友元,B是C的友元,不代表A是C的友元。
3.友元关系不能继承。
格式:friend class 类名;
例如:

(3)内部类

         如果一个类定义在另一个类的内部,这个内部的类就叫做内部类,外部的则是外部类。内部类是不属于外部类,不能通过外部类的对象去访问内部类的成员。但是内部类被外部类加上了类域,也受访问限定符的限制,需要用外部类的类名::来实例化对象。
        内部类是外部类的友元类,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
        sizeof(外部类名)求的是外部类所占空间大小,和内部类没有任何关系。
例如:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;

class A
{
public:
	class inner
	{
	public:
		void fun(A& a)
		{ 
			cout << a._a << endl;//内部类是外部类的友元类,可以访问私有成员
			cout << s_var << endl;//内部类直接访问外部类的静态成员
		}
	private:
		int _in=1;
	};
private:
	static int s_var;
	int _a=10;
};
int A::s_var = 0;

int main()
{
	cout<<sizeof(A::inner)<<endl;
	cout<<sizeof(A)<<endl;//外部类的大小不含内部类

	A a;
	A::inner in;
	in.fun(a);
	
	return 0;
}

输出:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值