当前位置: 首页 > 编程笔记 >

深入讲解C++中的构造函数

孙鑫鹏
2023-03-14
本文向大家介绍深入讲解C++中的构造函数,包括了深入讲解C++中的构造函数的使用技巧和注意事项,需要的朋友参考一下

C++构造函数
当创建一个对象时,往往需要做一些初始化工作,例如对数据成员赋值等。为了解决这个问题,C++提供了构造函数。

构造函数(Constructor)是一种特殊的成员函数,它的名字和类名相同,没有返回值,不需要用户调用(用户也不能调用),而是在创建对象时自动执行。构造函数的作用是在创建对象时进行初始化工作,最常见的就是对成员变量赋值。

一个构造函数的例子:

#include <iostream>
using namespace std;
class Student{
private:
  char *name;
  int age;
  float score;
public:
  //声明构造函数
  Student(char *, int, float);
  //声明普通成员函数
  void say();
};
//定义构造函数
Student::Student(char *name1, int age1, float score1){
  name = name1;
  age = age1;
  score = score1;
}
//定义普通成员函数
void Student::say(){
  cout<<name<<"的年龄是 "<<age<<",成绩是 "<<score<<endl;
}
int main(){
  //根据构造函数创建对象
  Student stu("小明", 15, 90.5f); //传参形式类似于函数调用
  stu.say();
  return 0;
}

运行结果:

小明的年龄是 15,成绩是 90.5

在类中我们定义了一个构造函数 Student(),它的作用是给3个 private 属性的成员变量赋值。在 main 函数中,我们根据构造函数创建了一个对象 stu;因为构造函数有参数,所以创建对象时要相应地传入实参,形式类似于函数调用。

读者要注意:一旦在类中定义了构造函数,那么创建对象时一定会被执行;如果构造函数有参数,创建对象时就要传参。

另外,构造函数主要用来进行初始化,没有返回值(有返回值没有任何意义),这就意味着:
不管是声明还是定义,函数名前面都不能出现返回值类型,即使是 void 也不允许;
函数体中不能有 return 语句。
默认构造函数

如果用户自己没有定义构造函数,那么编译器会自动生成一个默认的构造函数,只是这个构造函数的函数体是空的,也没有参数,不执行任何操作。比如上面的 Student 类,默认生成的构造函数如下:

Student(){}


一个类,必须有构造函数,要么用户自己定义,要么编译器自动生成。一旦用户自己定义了构造函数,不管它是 public 属性的,还是 private、protected 属性的,编译器都不再自动生成。上面的 Student 类,只有一个构造函数,就是我们自己定义的。
实际上,编译器只有在必要的时候才会生成默认构造函数,而且它的函数体一般不为空。默认构造函数的目的是帮助编译器做初始化工作,而不是帮助程序员。这是C++的内部实现机制,这里不再深究,初学者可以按照上面说的“一定有一个空函数体的默认构造函数”来理解。
构造函数的重载

和普通成员函数一样,构造函数是允许重载的。一个类可以提供多个构造函数,让用户在创建对象时进行选择,编译器会根据创建对象时传递的参数来确定调用哪一个构造函数。也就是说:
只有一个构造函数会被执行;
创建对象时提供的参数必须和其中的一个构造函数匹配,否则编译错误。

一个构造函数重载的例子:

#include <iostream>
using namespace std;
class Student{
private:
  char *name;
  int age;
  float score;
public:
  //声明构造函数
  Student();
  Student(char *, int, float);
  //声明普通成员函数
  void setname(char *);
  void setage(int);
  void setscore(float);
  void say();
};
//定义构造函数
Student::Student(){}
Student::Student(char *name1, int age1, float score1){
  name = name1;
  age = age1;
  score = score1;
}
//定义普通成员函数
void Student::setname(char *name1){
  name = name1;
}
void Student::setage(int age1){
  age = age1;
}
void Student::setscore(float score1){
  score = score1;
}
void Student::say(){
  cout<<name<<"的年龄是 "<<age<<",成绩是 "<<score<<endl;
}
int main(){
  //创建对象时初始化成员变量
  Student stu1("小明", 15, 90.5f);
  stu1.say();
  
  //调用成员函数来初始化成员变量的值
  Student stu2;
  stu2.setname("李磊");
  stu2.setage(16);
  stu2.setscore(95);
  stu2.say();
  return 0;
}

运行结果:

小明的年龄是 15,成绩是 90.5
李磊的年龄是 16,成绩是 95

类中定义了两个构造函数,一个带参数一个不带参数,它们是重载关系。当根据不带参数的构造函数创建对象时,不需要传参,成员变量不会被初始化,所以要调用成员函数来设置它们的值。

C++带参数的构造函数
不带参数的构造函数使该类的每一个对象都得到相同的初始值。

如果希望对不同的对象赋予不同的初始值,则需要使用带参数的构造函数,在调用不同对象的构造函数时,将不同的数据传给构造函数,以实现不同的初始化。

构造函数首部的一般格式为:
 

  构造函数名(类型1 形参1, 类型2 形参2, …)


由于用户是不能调用构造函数的,因此无法采用常规的调用函数的方法给出实参。实参是在创建对象时给出的。创建对象的一般格式为:

  类名 对象名(实参1, 实参2, …);

【例】有两个长方柱,其长、宽、高分别为12, 20, 25和10, 14, 20,求它们的体积。编写一个基于对象的程序,在类中用带参数的构造函数。

#include <iostream>
using namespace std;
class Box
{
  public :
  Box(int,int,int);
  int volume( );
  private :
  int height;
  int width;
  int length;
};
//声明带参数的构造函数//声明计算体积的函数
Box::Box(int h,int w,int len) //在类外定义带参数的构造函数
{
  height=h;
  width=w;
  length=len;
}
int Box::volume( ) //定义计算体积的函数
{
  return (height*width*length);
}
int main( )
{
  Box box1(12,25,30); //建立对象box1,并指定box1长、宽、高的值
  cout<<"The volume of box1 is "<<box1.volume( )<<endl;
  Box box2(15,30,21); //建立对象box2,并指定box2长、宽、高的值
  cout<<"The volume of box2 is "<<box2.volume( )<<endl;
  return 0;
}

程序运行结果如下:

The volume of box1 is 9000
The volume of box2 is 9450

可以知道:
带参数的构造函数中的形参,其对应的实参在定义对象时给定。
用这种方法可以方便地实现对不同的对象进行不同的初始化。
用参数初始化表对数据成员初始化

上面介绍的是在构造函数的函数体内通过赋值语句对数据成员实现初始化。C++还提供另一种初始化数据成员的html" target="_blank">方法——参数初始化表来实现对数据成员的初始化。这种方法不在函数体内对数据成员初始化,而是在函数首部实现。

例中定义构造函数可以改用以下形式:

  Box::Box(int h,int w,int len):height(h),width(w), length(len){ }


这种写法方便、简练,尤其当需要初始化的数据成员较多时更显其优越性。甚至可以直接在类体中(而不是在类外)定义构造函数。

 类似资料:
  • 本文向大家介绍js中Generator函数的深入讲解,包括了js中Generator函数的深入讲解的使用技巧和注意事项,需要的朋友参考一下 前言 Generator函数是es6提供的一种异步编程的解决方案,语法行为与传统函数完全不一样。 Generator函数有多种理解角度,从语法上,首先可以把它理解成,Generator函数是一个状态机,封装了多个内部状态。 执 行Generator函数会返回一

  • 本文向大家介绍C#中的协变与逆变深入讲解,包括了C#中的协变与逆变深入讲解的使用技巧和注意事项,需要的朋友参考一下 什么是协变与逆变 MSDN的解释: https://msdn.microsoft.com/zh-cn/library/dd799517.aspx 协变和逆变都是术语,前者指能够使用比原始指定的派生类型的派生程度更小(不太具体的)的类型,后者指能够使用比原始指定的派生类型的派生程度更大

  • 本文向大家介绍解析C#中的私有构造函数和静态构造函数,包括了解析C#中的私有构造函数和静态构造函数的使用技巧和注意事项,需要的朋友参考一下 私有构造函数 私有构造函数是一种特殊的实例构造函数。它通常用在只包含静态成员的类中。如果类具有一个或多个私有构造函数而没有公共构造函数,则其他类(除嵌套类外)无法创建该类的实例。例如: 声明空构造函数可阻止自动生成默认构造函数。注意,如果您不对构造函数使用访问

  • 本文向大家介绍MyBatis中$和#的深入讲解,包括了MyBatis中$和#的深入讲解的使用技巧和注意事项,需要的朋友参考一下 这是一次代码优化过程中发现的问题,在功能优化后发现部分数据查不到出来了,问题就在于一条sql上的#和$。 下图为两条sql: 从图上可以看出 wwlr.LabelId in(${showLabels}) 和 wwlr.LabelId in(#{showLabels}),其

  • 本文向大家介绍C#学习笔记整理_深入剖析构造函数、析构函数,包括了C#学习笔记整理_深入剖析构造函数、析构函数的使用技巧和注意事项,需要的朋友参考一下 构造函数、析构函数 构造函数: 1.若没提供任何构造函数,则系统会自动提供一个默认的构造函数,初始化所有成员为默认值(引用类型为空引用null,值类型为0,bool类型为false); 2.若提供了带参数的构造函数,则系统不提供默认的构造函数; 3

  • 本文向大家介绍Spring中@Autowire注入的深入讲解,包括了Spring中@Autowire注入的深入讲解的使用技巧和注意事项,需要的朋友参考一下 一直在思考spring的@Autowire注入属性时到底是按类型注入还是按名称注入,今天写了一个测试来证明一下。 定义接口TestService 定义接口实现:TestServiceImpl1和TestServiceImpl2 定义一个bean