C语言转C++的学习之路——类的认识1

这些天在看《C++ Primer Plus第6版》,觉得还是挺适合初学者的。现在看到了类相关的章节。边看边敲代码练练手,然后发现了一些好玩的东西,于是写下来分享给大家。
C++的类是什么?用C语言的角度来看,就是结构体+访问属性+特有函数(目前只看到第13章《类继承》的第1小节,这个观点可能是非常片面的)。为什么这么说呢?我们来看一个例子。(开发工具是VS2015)

#include "stdafx.h"
#include <stdio.h>
#include <iostream>
class A
{
private:
	int a;
	int b;
public:
	A()
	{
		a = 1;
		b = 2;
		c = 3;
		printf("A: this:%p a:%p b:%p c:%p\n\n", this,&a,&b,&c);
	}
	void show() const
	{
		std::cout << "a:" << a << " b:" << b << " c:" << c << std::endl;
	}	
public:
	int c;
};

class B : public A
{
private:
	int a2;
	int b2;

public:
	B()
	{
		a2 = 11;
		b2 = 12;
		c2 = 13;
		printf("B: this:%p a2:%p b2:%p c2:%p\n\n", this, &a2, &b2, &c2);
	}
	void show2() const
	{
		std::cout << "a2:" << a2 << " b2:" << b2 << " c2:" << c2 << std::endl;
	}
public:
	int c2;
};

int main()
{
	std::cout << "new A class" << std::endl;
	A *pa = new A;

	std::cout << "new B class" << std::endl;
	B *pb = new B;

	pa->show();
	pb->show2();

	delete pa;
	delete pb;

	getchar();
    return 0;
}

B类是从A类派生而来。我们看下运行结果:

当我们new一个A类赋给pa时,看他的this指针和各个成员变量的地址有什么关系。我们可以发现,A类的this指针和第一个成员a的地址是完全相等。b和a的地址相差4,c和b的地址相差4。如果我们把它看成一个结构体…从成员的地址上看,不就是一个结构体里面有3个int成员嘛。那this指针怎么就和pa的地址一致呢?我们从其他书籍中可以得到这么个结论:this指针是指向这个类实例的地址"——而一个结构体的地址不就是他的第一个变量的地址咯。想想看,是不是就这么一回事。

我们继续往下看,new B类时,由于B类是从A类派生的,而B的构造函数会优先构造他的基类A,所以当我们new B类时,会有两行输入,分别是其基类A和B的构造函数的输出。这个问题不大。那么我们来看下this指针和各个成员的地址的关系。
怎么基类A的this指针和B的this指针一样的?而且a,b,c的地址也是相差4。a2的地址和c也是相差4,b2,c2都是相差4。
从地址上看,分明就是a,b,c,a2,b2,c2的排列。是不是感觉直接就是B类的新成员直接放在其基类A的后面而已。而this指针就是第一个元素的地址。

说这些有什么用呢?
我们来看下这个问题:基类指针或者引用可以指向其派生类,反过来是不行的。为什么呢?
从上面的例子我们可以看到。类的指针就是指向他里面成员变量所在的地址。而类方法是通过this指针来调用成员变量的。用C语言来看,不就是把结构体的指针传到特定的函数里面嘛。如果把派生类B的地址赋值给基类A的指针,例如上面的代码A *pa2 = pb;而此时pa2只能调用基类里面的公有方法。想想看,不就是等同于直接派生类只调用了基类的方法。能有什么问题?毕竟这时的this指针的值就是派生类的地址。
那反过来呢?如果B*pb2 = (B*)pa;pb2->show2();会发生什么?请自行去试验。
是不是执行pb2->show2();后输出一些乱七八糟的数据。这是为什么呢?因为pa里面只有3个成员,而show2是打印后面3个变量的值,因此打印的数据是其身后未知的数据。我们来验证下这个说法,请看代码(我们只修改main函数)

int main()
{
	std::cout << "new A class" << std::endl;
	A *pa = new A;
	
	std::cout << "new B class" << std::endl;
	B *pb = new B;

	int *ptr = (int*)pa;
	ptr[3] = 50;
	ptr[4] = 60;
	ptr[5] = 70;

	B *pb2 = (B*)pa; 
	pb2->show2();

	//pa->show();
	//pb->show2();

	delete pa;
	delete pb;

	getchar();
    return 0;
}

运行下,是不是有个惊喜呢?弹出奔溃对话框。

抛开奔溃不看,是不是和我们上面的设想一样。我们强行给后3个int变量赋值。输出的就是我们的数据。至于为什么奔溃,这个就复杂了。简单一句话,你写了不该写的地方,肯定会奔溃。所以以上代码仅供学习用,不要放到自己的项目里面,不然出问题别说是我教你这么写的,我承担不起这个责任^_^

现在我们来总结下:什么是类对象。我们换个角度来看,当生成一个对象时,不管是new出来的还是直接声明的。都是开辟一个空间来存储里面的成员变量。然后用this指针指向这个空间的首地址。在后面调用这些成员时,通过this指针来定位所需的变量地址。而成员方法就是一些函数,只不过他们被限定在这个类相关来调用。是不是就验证那句话:不管实例化多少个类对象,里面的方法都只有一套因此,类是什么,只不过是结构体+访问属性+特有函数那么访问属性是什么呢?就是private、public、protected.这个在结构体里面可没有这玩意。而类的派生,就是扩大这个结构体和多加一些限定函数。目前看到的就这些,后面看到的再写了。
再次声明,以上只是片面的看法,只是片面的看法,片面的看法,片面的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值