C++构造函数

构造函数

一、思考

对象中成员变量初始值是多少?——随机值?还是0?

#include <stdio.h>
class Test
{
private:
    int i;
    int j;
public:
    int getI(){return i;}
    int getJ(){return j;}
};

Test t;

int main()
{
	Test t1;

	printf("t1.i = %d,t1.j = %d\n",t1.getI(),t1.getJ());
	printf("t.i = %d,t.j = %d\n",t.getI(),t.getJ());
	
    Test* pt = new Test;
	printf("pt->i = %d,pt->j = %d\n",pt->getI(),pt->getJ());
	delete pt;

	return 0;
}

——全局数据区的全局变量,初始为0;

——栈空间分配的局部变量,初始值为随机值;

——动态分配的堆空间的变量,初始值也为随机值。


小节:

从程序设计角度,对象只是变量,因此:

  • 上创建对象时,成员变量初始为随机值
  • 上创建对象时,成员变量初始值为随机值
  • 静态存储区创建对象时,成员变量初始值为0

二、对象的初始化

  • 一般而言,对象都需要一个确定的初始状态
  • 解决方案
    • 在类中提供一个publicinitialize函数
    • 对象创建后立即调用initialize函数进行初始化
#include <stdio.h>
class Test
{
private:
    int i;
    int j;
public:
	void initialize()
	{
		i = 1;
		j = 2;
	}
    int getI(){return i;}
    int getJ(){return j;}
};

Test t;

int main()
{
	t.initialize();
	
	Test t1;
	t1.initialize();

	printf("t1.i = %d,t1.j = %d\n",t1.getI(),t1.getJ());
	printf("t.i = %d,t.j = %d\n",t.getI(),t.getJ());

	Test* pt = new Test;
	pt->initialize();
	
	printf("pt->i = %d,pt->j = %d\n",pt->getI(),pt->getJ());

	delete pt;
	
	return 0;
}


——如果在定义之后,没有立即调用,就使用,会带来什么?能不能自动调用?

三、引入构造函数

C++中可以定义与类名相同的特殊成员函数——构造函数

  • 构造函数没有任何返回类型的声明
  • 构造函数在对象定义时自动被调用
#include <stdio.h>
class Test
{
private:
    int i;
    int j;
public:
	Test()
	{
		i = 1;
		j = 2;
        printf("Test() begin\n");
	}
    int getI(){return i;}
    int getJ(){return j;}
};

Test t;

int main()
{
	
	Test t1;

	printf("t1.i = %d,t1.j = %d\n",t1.getI(),t1.getJ());
	printf("t.i = %d,t.j = %d\n",t.getI(),t.getJ());

	Test* pt = new Test;
	
	printf("pt->i = %d,pt->j = %d\n",pt->getI(),pt->getJ());

	delete pt;
	
	return 0;
}

小结:

  • 每个对象在使用之前都应该初始化
  • 类的构造函数用于对象的初始化
  • 构造函数与类同名并且没有返回值
  • 构造函数在对象定义时自动被调用

四、带有参数的构造函数

  • 构造函数可以根据需要定义参数
  • 一个类中可以存在多个重载的构造函数
  • 构造函数的重载遵循C++重载的规则

友情提示:对象定义对象声明不同

  • 对象定义——申请对象的空间并调用构造函数

  • 对象声明——告诉编译器存在这样一个对象

#include <stdio.h>
class Test
{
public:
	Test()
	{
		printf("Test()\n");
	}
	Test(int v)
	{
		printf("Test(int v) = %d\n",v);
	}
};

int main()
{
	
	Test t;			//调用Test()
	Test t1(1);		//调用Test(int v)
	Test t2 = 1;	//调用Test(int v)
	
    t1 = t2;		//为赋值

	int i(100);		//为初始化i
	printf("i = %d\n",i);

	return 0;
}

五、构造函数的手动调用

  • 一般情况下,构造函数在对象定义时被自动调用
  • 特殊情况下,需要手动调用构造函数
#include <stdio.h>

class Test
{
    private:
    	int m_value;
    public:
    	Test()
        {
            printf("Test()\n");
            m_value = 0;
        }
    	Test(int v)
        {
            printf("Test(int v) = %d\n",v);
            m_value = v;
        }
    	int getValue()
        {
            return m_value;
        }
};

int main()
{
    Test ta[3] = {Test(),Test(1),Test(2)};//手工调用
    
    for(int i = 0; i < 3; i++)
    {
        printf("ta[%d].getValue() = %d\n",i,ta[i].getValue());
    }
    
    Test t = Test(100);			//又初始化的方式
    
    printf("t.getValue() = %d\n",t.getValue());
    
    return 0;
}

小实例

需求:开发一个数组类解决原生数组的安全性问题

  • 提供函数获取数组长度
  • 提供函数获取数组元素
  • 提供函数设置数组元素
//IntArray.cpp
#include "IntArray.h"

IntArray::IntArray(int len)
{
	m_pointer = new int[len];

	for(int i = 0; i < len; i++)
	{
		m_pointer[i] = 0;
	}
	m_legth = len;
}

//实现深拷贝
IntArray::IntArray(const IntArray& obj)
{
	m_legth = obj.m_legth;

	m_pointer = new int[obj.m_legth];

	for(int i = 0; i < obj.m_legth; i++)
	{
		m_pointer[i] = obj.m_pointer[i];
	}
}

int IntArray::length()
{
	return m_legth;
}

bool IntArray::get(int index, int& value)
{
	bool ret = ((0 <= index) && (index < length()));

	if(ret)
	{
		value = m_pointer[index];
	}

	return ret;
}

bool IntArray::set(int index, int value)
{
	bool ret = ((0 <= index) && (index < length()));

	if(ret)
	{
		m_pointer[index] = value;
	}

	ret;

}

void IntArray::free()
{
	delete[] m_pointer;
}

//IntArray.h
#ifndef _INTARRAY_H_
#define _INTARRAY_H_

class IntArray
{
private:
	int m_legth;
	int* m_pointer;
public:
	IntArray(int len);
	IntArray(const IntArray& obj);
	int length();
	bool get(int index, int& value);
	bool set(int index, int value);
	void free();
};

#endif// _INTARRAY_H_

/*
*越界保护
*构造函数,深层次拷贝
*/

#include <stdio.h>
#include "IntArray.h"

int main()
{
	IntArray a(5);

	for(int i = 0; i < a.length(); i++)
	{
		a.set(i, i+1);
	}

	for(int i = 0; i < a.length(); i++)
	{
		int vulue = 0;
		if(a.get(i, vulue)) //100越界了
		{
			printf("a[%d] = %d\n", i, vulue);
		}
	}

	//IntArray b = a;
	IntArray b(a);

	for(int i = 0; i < b.length(); i++)
	{
		int vulue = 0;
		if(b.get(i, vulue)) //100越界了
		{
			printf("b[%d] = %d\n", i, vulue);
		}
	}	


	//不要忘记释放
	a.free();
	b.free();
	
    return 0;
}

小结:

  • 构造函数可以根据需要定义参数
  • 构造函数之间可以存在重载关系
  • 构造函数遵循C++中的重载函数规则
  • 对象定义时会触发构造函数的调用
  • 在一些情况下可以手动调用构造函数

六、特殊的构造函数

  • 2个特殊的构造函数
    • 无参构造函数——没有参数的构造函数
    • 拷贝构造函数——参数为const class_name&的构造函数

1、无参构造函数

  • 当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空

2、拷贝构造函数

  • 当类中没有定义拷贝构造函数时,编译器默认一个拷贝构造函数,简单的进行成员变量的值赋值

  • 意义

    1. ==兼容C语言的初始化方式 ==

    2. 初始化行为能够符合预期的逻辑

    3. 浅拷贝

      • 拷贝后对象的物理状态相同

      编译器提供的拷贝构造函数只进行浅拷贝!

    4. 深拷贝

      • 拷贝后对象的逻辑状态相同
//19-1.cpp
#include <stdio.h>

class Test
{
private:
	int i;
	int j;
public:
	int getI()
	{
		return i;
	}
	int getJ()
	{
		return j;
	}
	/*
	Test(const Test& t)
	{
		i = t.i;
		j = t.j;
	}
	Test()
	{
	}
	*/
};

int main()
{
	Test t1;
	Test t2 = t1;
	
	printf("t1.i = %d,t1.j = %d\n",t1.getI(),t1.getJ());
	printf("t2.i = %d,t2.j = %d\n",t2.getI(),t2.getJ());

	return 0;
}

//19-2.cpp
#include <stdio.h>

class Test
{
private:
	int i;
	int j;
	int* p;
public:
	int getI()
	{
		return i;
	}
	int getJ()
	{
		return j;
	}
	int* getP()
	{
		return p;
	}
	
	Test(const Test& t)
	{
		i = t.i;
		j = t.j;
		p = new int;

		*p = *t.p;
	}
	Test(int v)
	{
		i = 1;
		j = 2;
		p = new int;

		*p = v;
	}
	void free()
	{
		delete p;
	}
};

int main()
{
	Test t1(3);
	//Test t2 = t1;//此适类似于C语言的赋值
	
	Test t2(t1);
	

	printf("t1.i = %d,t1.j = %d,t1.p = %p\n",t1.getI(),t1.getJ(),t1.getP());
	printf("t2.i = %d,t2.j = %d,t2.p = %p\n",t2.getI(),t2.getJ(),t2.getP());

	//在内存中的地址不同,但值相同
	printf("t1.i = %d,t1.j = %d,*t1.p = %d\n",t1.getI(),t1.getJ(),*t1.getP());
	printf("t2.i = %d,t2.j = %d,*t2.p = %d\n",t2.getI(),t2.getJ(),*t2.getP());
	
	//若释放t1.p,则调用t2.p会失败

	t1.free();
	t2.free();
	
	return 0;
}


3、什么时候需要进行深拷贝

  • 对象中有成员指代系统中的资源
    • 成员指向了动态内存空间
    • 成员打开了外存中的文件
    • 成员使用了系统中的网络端口

4、一般性原则

​ 自定义拷贝构造函数,必然需要实现深拷贝。

小结:

C++编译器会默认提供构造函数

无参构造函数用于定义对象的默认初始状态

拷贝构造函数在创建对象时拷贝对象的状态

对象的拷贝有浅拷贝深拷贝

  • 浅拷贝,拷贝后对象的物理状态相同
  • 深拷贝,拷贝后对象的逻辑状态相同
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值