C++之构造函数

1 构造函数可以分为两种:

  1. 普通构造函数
  2. 复制构造函数(又名拷贝构造函数)

2 构造函数的特性:

  1. 特殊的函数
  2. 创建对象时,自动调用(一个对象只能调用一次构造函数,只有在创建时自动调用(隐式调用)构造函数不能显示调用)
  3. 如果我们没有定义构造函数,系统会提供默认的构造函数(提供两个:一个普通构造, 一个复制构造),提供的普通构造函数是无参的,一旦我们自定义普通构造函数,那么这个普通构造函数就不再提供了
  4. 创建对象时,必须提供一个匹配的构造函数,否则无法创建对象
  5. 构造函数支持重载
  6. 构造函数支持参数默认值(如果同时存在函数重载 参数默认值,可能会出现调用的冲突)

3 构造函数的格式:

普通函数格式:返回值 函数名(形参列表);
构造函数格式:类名(形参列表);

  1. 构造函数名必须与类名相同
  2. 构造函数没有返回值类型
    注:没有返回值,返回值类型为: void
    没有返回值类型,连void也没有
class Point {
	int x;
	int y;
public:
	Point()	//构造函数
	{
		std::cout<<"无参构造函数"<<std::endl;
	}
}; 		

4 构造函数的作用

  1. 给对象一个标识符
  2. 为对象的数据成员分配空间
  3. 对数据成员初始化(或赋值):需要程序员自己写构造函数
 class Point {
	int x;
	int y;
public:
	Point()	//构造函数
	{
		x = y = 0;	//赋值
		std::cout<<"无参构造函数"<<std::endl;
	}
}; 

5 构造函数的注意事项

  1. 一个对象只能调用一次构造函数
  2. 通常我们都会自定义构造函数(带参),用于创建对象的初始化
  3. 由于我们也习惯不初始化创建对象,所以我们也要自定义无参构造

6 初始化表达式

本质:表达式
理解:用来对数据成员初始化的表达式
用来给对象的数据成员初始化的
分配空间的同时,写入数据:初始化

  1. 初始化表达式只能用于构造函数中
  2. 初始化表达式位于构造函数形参列表后,函数体前形参列表后,加:间隔
  3. 初始化的顺序执行顺序不是由成员变量在初始化表的顺序决定的,而是由成员变量在类中被声明时的顺序决定的(下面中的x先声明,所以x先初始化)
  4. 初始化表达式式中的变量顺序最好与其被声明的顺序一致
class Point {
	int x;
	int y;
public:
	Point();
};
// Point::Point(int a, int b):初始化表达式1,初始化表达式2
//{
//}
 Point::Point(int a, int b): x(a)y(b)
{
}

初始化的特殊性:

  1. 分配空间的同时写入数据
  2. 一次性写入多个数据到空间中
    常见:数据的初始化
    结构体的初始化
  3. 特殊的空间
    const int a = 80;
    int b = 80;
    int &p = b;

创建对象的方式:

class Point {
	int x;
	int y;
public:
	Point();
};

Point::Point()
{
	std::cout<<"无参构造函数"<<std::endl;
}

int main()
{
	//Point ss();   //不是创建对象, 而是函数声明
	Point ss;   //调用无参构造函数
	Point *p = new Point;//调用无参构造函数
	Point *q = new Point();//调用无参构造函数,和new Point等价
}

7 复制构造函数

本质:构造函数
理解:做复制操作的构造函数
如果我们没有自定复制构造函数,系统会提供一个默认的
当创建对象时,将一个同类型的对象整体赋值时,就会调用复制构造:将数据成员一一对应的复制过去

复制构造函数:
参数: 一个,通常是该类的常引用

class Point
{
    int x;
    int y;
public:
    Point(){}
    Point(const Point &n)
    {
        std::cout<<"复制构造"<<std::endl;
        this->x = n.x;
        this->y = n.y;

    }
};

int main()
{
    Point a;
    Point b(a); //调用复制构造函数
    Point c = a; //调用复制构造函数
	Point d;
	d = a;	//不是复制构造,初始化的时候已经调用了普通构造,这里是赋值运算符重载函数
}

构造函可以有很多,但是复制构造函数只有一个
注意:自定义了复制构造函数之后,系统就不会提供默认的普通构造函数,所以自定了复制构造函数就必须自定义普通构造函数
什么时候需要自定复制构造:

  1. 通常只要类的内部不出现堆区空间是,就使用默认的复制构造就行了;
  2. 如果类的内部出现成员指向堆区空间,就必须自定义了
class Point
{
    int x;
    int y;
    char* name = NULL;
public:
    Point(){}
    Point(int a, const char *n){
        int len = strlen(n) + 1;
        name = new char[len];
        strcpy_s(name, len, n);
    }
    Point(const Point &n)
    {
        std::cout<<"复制构造"<<std::endl;
        this->x = n.x;
        this->y = n.y;
        int len = strlen(n.name) + 1;
        this->name = new char[len];
        strcpy_s(this->name, len, n.name);

    }
    ~Point()
    {
        if(name)
        {
            delete name;
            name =NULL;
        }
    }
};

int main()
{
    Point a(1, "hehe");
    Point b(a);
}

8 总结

  1. 通常普通构造函数都会自定义:1个无参 1个带参
  2. 通常析构函数、复制构造函数可以不写(使用默认的)
  3. 当类内部出现指针成员指向堆区空间,存在内部分配堆区空间,通常就需要自定义析构和复制构造了
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值