重温C++小知识----- 类

实验室的小伙伴都回家过中秋了,坐在实验室有点无聊,王者和梦幻打了几把就不想打了,仿佛总感觉少了点什么,闲着也是闲着,一边祝筒子们节日快乐,一边想想最近被面试官问到C++这一块,确实很简单很基础,但是答不完整却又是一个坑,这么简单的问题答不好,面试官也是呵呵了,这种问题平时没注意,被问到的时候还真的一时回答的不是很完整,这也算是平时学习不认真,没有深入探究的小代价吧,小总结一下:

按套路出牌的话,首先会问class 和struct 的区别:

在C++中,class 和 struct 都是定义类的关键字,形式上看起来不一样,本质上唯一的区别就是默认访问权限不一样,class的默认访问权限是private,而struct 的默认访问权限是public 

在C语言中,结构体struct 中是不能有函数,固没有构造函数,析构函数以及this 指针,并且结构体不能被继承

在C++中,对C语言中的struct 进行了扩充,使得struct 可以有构造函数,析构函数等,并且能够被继承,struct 与class 最本质的区别就是默认访问权限


接下来就是,在C++中,一个类除了成员变量,有哪些函数?

在C++的类中,除了成员变量,还有默认不带参数的构造函数,拷贝构造函数,赋值运算符,析构函数,当你创建一个空类,不做任何事情,编译器也会自动生成这几个函数,其中构造函数和拷贝构造函数的名字和类名要一致。

构造函数的任务是初始化类对象的数据成员,只要类的对象被创建,构造函数就会被调用,在类中,默认的构造函数是不带参数的,如果自己定义了不带参数的构造函数,调用的将是你定义的构造函数,当然也可以定义带参数的构造函数来满足特定的需求


拷贝构造函数主要目的是用一个已经存在的对象去初始化另外一个对象,拷贝构造函数的第一个参数是自身类的引用,这是因为拷贝构造函数在以下情况会被使用:

  1. 对象以值传递方式传入函数参数
  2. 对象以值传递方式从函数返回
  3. 对象需要通过另外一个对象进行初始化

因此,如果传入的不是引用,而是普通的对象,接着又会调用拷贝构造函数,拷贝构造函数就会被一直递归调用

默认的拷贝构造函数是浅拷贝的,浅拷贝和深拷贝的区别:

浅拷贝:就是在将B对象赋值给A的时候,如果B的成员变量已经分配了内存空间,A 中对应的成员变量直接指向B 分配的内存空间,这样A和B成员变量的指针都指向了同一块空间。浅拷贝的问题是:如果B将内存释放了,A 对应成员变量的指针就变成野指针。

深拷贝:在将B对象赋值给A的时候,为A的成员变量重新开辟新的资源空间,将B的值赋值过来就OK,这样B释放掉自己的内存空间是,A也不会有问题。


赋值运算符从名字上看就是将一个对象赋值给另外一个对象,本质上也是一个函数,它与拷贝构造函数的区别就是赋值运算符赋值的对象必须是先要存在的,如:

        A a;
	A b;
	b = a;  //调用的是赋值运算符,a,b已经存在
而以下情况则调用的是拷贝构造函数,如

        A a;
	A b = a;  //调用拷贝构造函数,b最开始并没存在
	A c(a);	  //拷贝构造函数

浅拷贝代码示例

class A
{
public:
	//构造函数
	A(){}
	A(int x)
	{
		m_val = x;
		m_ptr = new char[m_val];
	}
	//拷贝构造函数,默认浅拷贝
	A(const A&a)
	{
		m_val = a.m_val;
		m_ptr = a.m_ptr;	//两个指针都指向同一块内存空间
	}

	//赋值运算符
	A &operator = (const A&a)
	{
		if (this ==&a)
		{
			return *this;
		}
		else
		{
			auto newp = new char(*a.m_ptr);
			delete m_ptr;
			m_ptr = newp;
			m_val = a.m_val;
			return *this;	
		}
		
	}
	//析构函数
	virtual ~A()
	{
		delete[]m_ptr;
		m_ptr = NULL;
	}
private:
	int m_val;
	char* m_ptr;
};


int _tmain(int argc, _TCHAR* argv[])
{
	A b = A(100);	//拷贝构造函数
	A c= A();		
	A a = b;		//拷贝构造函数
	c = a;			//赋值运算符
	return 0;
}


运行结果如下,调试结果可得a,b的指针都指向同一块内存

深拷贝示例代码:

	A(const A&b)
	{
		m_val = b.m_val;
		m_ptr = new char[m_val];
		for (int i = 0; i < m_val; i++)
		{
			m_ptr[i] = b.m_ptr[i];
		}
	}
调试结果如下:a,b的指针指向的是不同的内存块



接下来分析一下带有虚函数的类以及其成员变量在内存中的分布情况,先看两个不带虚函数的类Concrete1和Concrete2

class Concrete1
{
public:
	//...
private:
	int val;
	char bit1;
};

class Concrete2:public Concrete1
{
public:
	//...
private:
	char bit2;
};
在32位的机器中,,每一个Concrete1的对象大小是8 bytes,Concrete2的对象大小是12 bytes ,如果有另一个类Concrete3继承Concrete2

class Concrete3 :public Concrete2
{
public:
	//...
private:
	char bit3;
};
那么此时每一个Concrete3的对象大小将是16,对象布局如图, 图中Concrete1,Concrete2,Concrete3对象中最后的3 bytes都是用于边界调整的,由处理器决定(32位机器里按4个byte 对齐)

对于带有虚函数的类,Concrete1和Concrete2带有虚函数,如下

class Concrete1
{
public:
	//...
	virtual void func1()
	{
		cout << "func1...." << endl;
	}
	virtual void func11()
	{
		cout << "func11...." << endl;
	}
private:
	int val;
	char bit1;
};

class Concrete2:public Concrete1
{
public:
	//...
	virtual void func2()
	{
		cout << "func2...." << endl;
	}
private:
	char bit2;
};
则每一个Concrete1对象和Concrete2对象的大小为 12 bytes 和16 bytes



接下来这一块就是关于多态的了,首先看一下重载和重写的区别








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值