c++类和对象中的一些细节(二)

1、const修饰类的成员函数

        有如下场景

class A 
{
public:
	void print()
	{
		printf("%d\n", _a);
	}
private:
	int _a;
};

void func(const A& obj)
{
	obj.print();
}

        注意在func内对象obj调用print是会发生编译错误的,这是因为obj被const修饰,是一个只读的对象,obj.print()执行时,将obj的地址传给print,也就是print的第一个参数this,而这个this没有被const修饰,*this是可读可写的,发生了权限的扩大,这是不被编译通过的。

        怎么解决呢?在print函数后加上const关键字,这个关键字虽然表面上用于修饰成员函数,本质上是为了修饰成员函数的第一个参数this,构成隐式的const *this,让*this只能读不能写,同时也外部的const A& obj相匹配。下面这种写法是不会发生报错的。

class A 
{
public:
	void print()const
	{
		printf("%d\n", _a);
	}
private:
	int _a;
};

void func(const A& obj)
{
	obj.print();
}

        我们的自定义类型的成员函数,如果不对自身成员变量进行写操作,都推荐在函数后边加上const关键字,这样外部的不论是带const还是不带const修饰的变量都可以调用这个成员函数。

2、初始化列表

        我们自定义类型的初始化,可以调用自己定义的普通的构造函数或者拷贝构造函数(如果不自己实现,编译器也会默认生成,不过是浅拷贝)。如下。

class letter 
{
public:
	letter(int a, int b, int c)
	{
		_a = a;
		_b = b;
		_c = c;
	}
	letter(const letter& L)
	{
		_a = L._a;
		_b = L._b;
		_c = L._c;
	}
private:
	int _a;
	int _b;
	int _c;
};

        除此之外c++还为我们提供了另一种写法,初始化列表。

class letter 
{
public:
	letter(int a, int b, int c)
		:_a(a), _b(b), _c(c)
	{}
	letter(const letter& L)
		:_a(L._a), _b(L._b), _c(L._c)
	{}		
private:
	int _a;
	int _b;
	int _c;
};

        这种写法除了提升我们代码的可读性。

        需要注意的几点:

        有三类成员变量是必须在初始化列表里进行初始化的:(1)引用成员变量(没错,成员变量可以是引用);(2)const修饰的成员变量;(3)没有默认构造函数的自定义类型成员变量(默认构造函数是指不需要传参的构造函数)。为什么?以上三类成员变量共性都是在定义时必须初始化,利用初始化列表就可以在定义时初始化,而在函数体内的动作属于赋值,已经是定义之后的动作了。

        成员变量初始化的顺序与初始化列表里的顺序无关,取决于成员变量在类内声明的顺序。有如下例子。

class A
{
public:
	A(int a)
		:_a1(a),_a2(_a1)
	{}
	void print() 
	{
		printf("%d %d\n", _a1, _a2);
	}
private:
	int _a2;
	int _a1;
};

int main()
{
	A aa(1);
	aa.print();
	return 0;
}

        类A内的成员变量_a2和_a1先后声明,当我们构造对象aa时,先用_a1初始化成员变量_a2,此时_a1是随机值,因此_a2被初始化为随机值,随后用1初始化_a1,因此_a1为1。代码运行结果如下。

3、explicit关键字修饰成员函数

        我们实例化一个类是有这种写法的。

class A
{
public:
	A(int a)
		:_a1(a)
	{}
private:
	int _a1;
};

int main()
{
	A aa = 1;
	return 0;
}

        在初始化aa时,右值1发生了隐式类型转化,生成了一个A类型的临时对象,我们姑且将其命名为tmp,随后用tmp初始化了aa。

        如果我们不希望发生隐式类型转换,可以在类的构造函数前加上explicit关键字。

           这样编译器会提示不存在将int转换为A的适当构造函数,编译不通过。

4、static关键字修饰成员变量

class A
{
public:
	explicit A(int a)
		:_a1(a)
	{}
//private:
	int _a1;
	static int _a2;
};
int A::_a2 = 1;

int main()
{
	printf("%p\n", &A::_a2);
	printf("%d\n", sizeof A);
	return 0;
}

        这个static变量必须在外部定义,类内的static int _a2;语句只是声明。定义了之后这个静态变量的生命周期不依赖与对象的创建与销毁。它不是在存储在栈上的,而是存储在数据段上的,因此不论是否创建对象,它都是存在的,我们可以在不创建对象的情况下取到这个静态变量的地址(前提是这个静态变量的访问限定符时public,如果是private或者protected就不行)。所以可以理解为这个静态变量是属于类的,而不是属于对象的。当我们通过sizeof操作查看A类型的大小时,可以看到是4,而不是8,进一步说明自定义类型内声明的静态变量不会放在每一个对象内。我们可以通过创建对象访问这个静态变量,注意不论创建多少个对象,访问这个静态变量访问的都是同一块空间。上面一段代码的运行结果如下。

5、static关键字修饰成员函数

        成员函数前面如果加上了static关键字,那么参数里将没有隐含的this指针,即我们的对象在调用这个函数时,没有将自己的地址传给static函数,这个函数自然就不能访问我们类内的非static成员变量以及非static成员函数(可以访问static变量和static成员函数,因为static的存在不依赖于对象的创建)。

        总结一下static关键字修饰的成员变量和成员函数,与普通的静态变量和全局函数没有本质的差别,存储的空间都是相同的,只是通过类中的访问限定符来收到了类域的访问限制。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值