【C++】c++中的六个默认函数——构造函数

类的6个默认的成员函数包括:

构造函数、析构函数、拷贝构造函数、赋值运算符重载函数、取地址操作符重载、const修饰的取地址操作符重载。(但是重点讲前四个)

 

  • 构造函数(可以重载)

C++中,有一种特殊的成员函数,它的名字和类名相同,没有返回值,不需要用户显式调用(用户也不能手动调用),而是在创建对象时自动执行。这种特殊的成员函数就是构造函数(Constructor

class Student{
private:
    char *m_name;
    int m_age;
    float m_score;
public:
    //声明构造函数
    Student(char *name, int age, float score);
    //声明普通成员函数
    void show();
};

//定义构造函数
Student::Student(char *name, int age, float score){
    m_name = name;
    m_age = age;
    m_score = score;
}
//定义普通成员函数
void Student::show(){
    cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<endl;
}

int main(){
    //创建对象时向构造函数传参
    Student stu("Li", 15, 92.5f);
    stu.show();
    //创建对象时向构造函数传参
    Student *pstu = new Student("Hua", 16, 96);
    pstu -> show();

    return 0;
}

Student 类中定义了一个构造函数Student( ),它的作用是给三个 private 属性的成员变量赋值。要想调用该构造函数,就得在创建对象的同时传递实参,并且实参由( )包围,和普通的函数调用非常类似。

 

  • 构造函数的重载

和普通成员函数一样,构造函数是允许重载的。一个类可以有多个重载的构造函数,创建对象时根据传递的实参来判断调用哪一个构造函数。构造函数的调用是强制性的,一旦在类中定义了构造函数,那么创建对象时就一定要调用,不调用是错误的。如果有多个重载的构造函数,那么创建对象时提供的实参必须和其中的一个构造函数匹配;反过来说,创建对象时只有一个构造函数会被调用

#include <iostream>
#include <string>
using namespace std;

class Student{
private:
    char *m_name;
    int m_age;
    float m_score;
public:
    //声明构造函数
    Student(char *name, int age, float score);
	//声明
	Student();
    //声明普通成员函数
    void show();
	void setname(char *name);
	void setage(int age);
	void setscore(float score);
};
//定义构造函数
Student::Student(char *name, int age, float score){
    m_name = name;
    m_age = age;
    m_score = score;
}
//构造函数的重载
Student::Student()
{
	m_name = NULL;
	m_age = 0;
	m_score = 0.0;
}

void Student::setname(char *name)
{
	m_name = new char[strlen(name) + 1]();
	strcpy_s(m_name, strlen(name) + 1, name);//用strcpy_s (oldstr, len, newstr)似乎更安全
}

void Student::setage(int age){
    m_age = age;
}

void Student::setscore(float score){
    m_score = score;
}

//定义普通成员函数
void Student::show(){
    cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<endl;
}
int main(){
    //创建对象时向构造函数传参
    Student stu("Ming", 15, 92.5f);
    stu.show();
    //创建对象时向构造函数传参
    Student *pstu = new Student();
    //pstu -> show();
    pstu -> setname("Hua");
    pstu -> setage(16);
    pstu -> setscore(96.7f);
    pstu -> show();
}

构造函数Student()将各个成员变量的值设置为空,它们是重载关系。根据Student()创建对象时不会赋予成员变量有效值,所以还要调用成员函数 setname()、setage()、setscore() 来给它们重新赋值。

 

  • 默认构造函数(是一个浅拷贝)

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

一个类必须有构造函数,要么用户自己定义,要么编译器自动生成。一旦用户自己定义了构造函数,不管有几个,也不管形参如何,编译器都不再自动生成。

最后需要注意的一点是,调用没有参数的构造函数也可以省略括号。在栈上创建对象可以写作Student stu()Student stu,在堆上创建对象可以写作Student *pstu = new Student()Student *pstu = new Student,它们都会调用构造函数 Student()。

 

  • 参数初始化列表
  •  

  • 定义构造函数时并没有在函数体中对成员变量一一赋值,其函数体为空(当然也可以有其他语句),而是在函数首部与函数体之间添加了一个冒号: 后面紧跟m_name(name), m_age(age), m_score(score)语句,这个语句的意思相当于函数体内部的m_name = name; m_age = age; m_score = score;语句,也是赋值的意思。

  • //参数初始化表
    Student::Student(char *name, int age, float score): m_name (name), 
    	m_age (age), m_score (score){
    }

    注意,参数初始化顺序与初始化表列出的变量的顺序无关,它只与成员变量在类中声明的顺序有关。

#include <iostream>
using namespace std;
class Fun{
private:
    int m_a;
    int m_b;
	/*
	改为: 
	int m_b;
	int m_a; 输出就会是用户想得到的数值
	*/
public:
    Fun(int b);
    void show();
};
Fun::Fun(int b): m_b(b), m_a(m_b){ //调用顺序只和Fun类中声明的顺序有关
}
void Fun::show(){ cout<<m_a<<", "<<m_b<<endl; }
int main(){
    Fun re(100);
    re.show();
    return 0;
}

 

  • 初始化 const 成员变量

    初始化 const 成员变量的唯一方法就是使用参数初始化表,例如下面的例子:

#include <iostream>
using namespace std;
class Fun{
private:
    const int m_a;//初始化 const 成员变量
    int m_b;
public:
    Fun(int b);
    void show();
};
Fun::Fun(int b): m_a(b){ 
	m_b = b;
}
void Fun::show(){ cout<<m_a<<", "<<m_b<<endl; }
int main(){
    Fun re(100);
    re.show();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_41576955/article/details/84707806