c++ 类的特殊成员函数:构造函数(一)

文章讨论了C++中构造函数的作用,包括在创建对象时自动执行、初始化成员变量、构造函数的重载以及无参构造函数的默认生成。通过示例代码,解释了如何通过构造函数初始化列表更高效地初始化成员变量,同时提到了在特定情况如常量成员、引用类型、类或结构体成员以及继承中的初始化应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

构造函数是类的一种特殊的成员函数,它在每次创建类的新对象时被执行。与类名同名,没有返回值,可以被重载,可以有参,通常用来做初始化工作。

构造函数:

1. 创建类的对象,就会执行类的构造函数

默认情况下,编译器会为每个类添加无参构造函数。
Engineer.h

#ifndef PCL_STUDY_ENGINEER_H
#define PCL_STUDY_ENGINEER_H

#include <iostream>

class Engineer {
public:
    std::string ID;
    std::string post;

    Engineer();
    Engineer(std::string ID);
    Engineer(std::string ID,std::string post);
    
};


#endif //PCL_STUDY_ENGINEER_H

无参构造,前面得加 public,不然创建对象时报错:

Calling a private constructor of class 'Engineer' 
implicitly declared private here

Engineer.cpp

Engineer::Engineer(){
    std::cout << "无参构造 ..." << std::endl;
}

Engineer::Engineer(std::string ID){
    std::cout << "有参构造 --> 单 ..." << std::endl;
}

Engineer::Engineer(std::string ID,std::string post){
    std::cout << "有参构造 --> 双 ..." << std::endl;
}

Create_object.cpp

#include "Engineer.h"

int main(){
    
    Engineer Engineer_object;
    Engineer Engineer_object1;
    
    return 0;
};

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(pcl_study)


set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_C_COMPILER /usr/bin/x86_64-linux-gnu-gcc) #c编译器
set(CMAKE_CXX_COMPILER /usr/bin/x86_64-linux-gnu-g++) #c++编译器

# ${CMAKE_CURRENT_SOURCE_DIR} == ${PROJECT_SOURCE_DIR}

# 设置输出根目录为# /${CMAKE_BUILD_TYPE}
set(OUTPUT_DIRECTORY_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
# 设置可执行程序输出到bin目录
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OUTPUT_DIRECTORY_ROOT}/bin" CACHE PATH "Runtime directory" FORCE)
# 设置库文件输出到lib目录
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${OUTPUT_DIRECTORY_ROOT}/lib" CACHE PATH "Library directory" FORCE)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${OUTPUT_DIRECTORY_ROOT}/lib" CACHE PATH "Archive directory" FORCE)

add_executable(create_object Create_object.cpp Engineer.cpp)

运行的时候:

无参构造 ...
无参构造 ...

创建了两个对象,执行了两次无参构造。

2. 构造函数重载,成有参构造

Create_object.cpp

#include "Engineer.h"

int main(){

    Engineer Engineer_object;
    Engineer Engineer_object1;

    Engineer Engineer_object2("00001");
    Engineer Engineer_object3("00001","Algorithm engineer");

    return 0;
};

运行结果:

无参构造 ...
无参构造 ...
有参构造 -->...
有参构造 -->...

注意:

Engineer Engineer_object0();

是函数的声明,声明一个返回值类型为Engineer ,没有参数的 Engineer_object0 函数,
不是创建一个对象.
clion 的提示词也可看出

Function 'Engineer_object0' is never used 

3. 只要在类中创建了构造函数,编译器就不会添加无参构造函数

Engineer.h

#ifndef PCL_STUDY_ENGINEER_H
#define PCL_STUDY_ENGINEER_H

#include <iostream>

class Engineer {
    std::string ID;
    std::string post;

public:

//    Engineer();
    Engineer(std::string ID ID);
    Engineer(std::string ID ID,std::string post);

    
};

#endif //PCL_STUDY_ENGINEER_H

此时

Engineer Engineer_object;

报错:

No matching constructor for initialization of 'Engineer' 
candidate constructor not viable: requires single argument 'ID', but no arguments were provided 
candidate constructor (the implicit copy constructor) not viable: requires 1 argument, but 0 were provided 
candidate constructor (the implicit move constructor) not viable: requires 1 argument, but 0 were provided 
candidate constructor not viable: requires 2 arguments, but 0 were provided

没有用于初始化“Engineer”的匹配构造函数
候选构造函数不可行:需要单个参数“ID”,但未提供任何参数
候选构造函数(隐式复制构造函数)不可行:需要1个参数,但提供了0个
候选构造函数(隐式移动构造函数)不可行:需要1个参数,但提供了0个
候选构造函数不可行:需要2个参数,但提供了0个

4. 构造函数是用来初始化类的成员变量

4.1 先创建对象,再对对象的成员赋值

...
Engineer Engineer_object;
Engineer_object.ID = "00000";
Engineer_object.post = "Algorithm engineer";
...

4.2 通过有参构造函数,创建对象的同时,赋值给成员变量

Create_object.cpp

Engineer Engineer_object3("00001","Architecture engineer");

Engineer.cpp

...
Engineer::Engineer(std::string ID,std::string post){
    ID = ID;
    post = post;
    std::cout << "有参构造 --> 双 ..." << std::endl;
    std::cout << "--" <<post << " 的工号是 : "<< ID<<"--"<< std::endl;
}
...

运行结果:

有参构造 -->...
--Architecture engineer 的工号是 : --

结果不完整,根据参数范围的就近原则

ID = ID;
post = post;

上面的两个ID 都是函数的局部变量std::string ID, 两个 post 都是函数的局部变量std::string post ,与上面有参构造函数外的的类的成员变量没关系.

4.2.1 可改成:

Engineer::Engineer(std::string number,std::string position){
    ID = number;
    post = position;
    std::cout << "有参构造 --> 双 ..." << std::endl;
    std::cout << "--" <<post << " 的工号是 : "<< ID<<"--"<< std::endl;
}

运行结果:

--Architecture engineer 的工号是 : 00002--

4.2.2 也可改成:

Engineer::Engineer(std::string ID,std::string post){
    Engineer::ID = ID;
    Engineer::post = post;
    std::cout << "有参构造 --> 双 ..." << std::endl;
    std::cout << "--" <<post << " 的工号是 : "<< ID<<"--"<< std::endl;
}

运行结果:

--Architecture engineer 的工号是 : 00002--

4.2.3 还可改成:

Engineer::Engineer(std::string ID,std::string post){
    this->ID = ID;
    this->post = post;
    std::cout << "有参构造 --> 双 ..." << std::endl;
    std::cout << "--" <<post << " 的工号是 : "<< ID<<"--"<< std::endl;
}

运行结果:

--Architecture engineer 的工号是 : 00002--

上面的三种本质上是先声明,再赋值.类似于:

int num;
num = 18;

5. 初始化列表的形式完成类的成员变量的初始化

在上面类的成员变量的初始化时,是在构造函数的函数体里面完成的。如果使用初始化列表,那么成员变量的初始化赋值是在函数体执行前完成,避免了先创建对象再对成员变量赋值的过程,提高了效率和代码可读性。
初始化列表的形式为:

Engineer::Engineer(std::string ID,std::string post):ID{ID},post{post}{

    std::cout << "有参构造 --> 双 ..." << std::endl;
    std::cout << "--" <<post << " 的工号是 : "<< ID<<"--"<< std::endl;
}

也可为: 大括号变成小括号

Engineer::Engineer(std::string ID,std::string post):ID(ID),post(post){

    std::cout << "有参构造 --> 双 ..." << std::endl;
    std::cout << "--" <<post << " 的工号是 : "<< ID<<"--"<< std::endl;
}

类似为:

int num {18};
int num (18);
int num = 18;

初始化列表有一个优点是: 防止类型收窄,换句话说就是精度丢失

#include <iostream>

using namespace std;

class Demo {
public:
    Demo(int num_i, double num_d,char a): num_i(num_i), num_d(num_d), a(a) {}
    void show() { cout << "num_i: " << num_i << ", num_d: " << num_d << ", a: " << a << endl; }
private:
    int num_i;
    double num_d;
    char a;
};

int main() {
    Demo demo(20, 1.75,'a');
    demo.show();

    Demo demo1(20.75, 1,50);
    demo1.show();

    Demo demo2('a', 'b','s');
    demo2.show();

    Demo demo3('str', 'str',1.75);
    demo3.show();

    return 0;
}

执行结果:

num_i: 20, num_d: 1.75, a: a
num_i: 20, num_d: 1, a: 2
num_i: 97, num_d: 98, a: s
num_i: 7566450, num_d: 7.56645e+06, a: 
Demo demo4("str", "str",1.75);
demo4.show();

此时报错:

No matching constructor for initialization of 'Demo'

使用到构造函数初始化列表的情况:

5.1 类的成员变量是常量

常量成员变量在创建后不能修改,因此必须在定义时进行初始化

class Circle {
private:
    const double PI;
    double radius;

public:
    // 使用初始化列表进行 PI 的初始化
    Circle(double r) : PI(3.1415), radius(r) {}

    double getArea() { return PI * radius * radius; }
};

5.2 类的成员变量是引用类型

引用类型成员变量创建时必须初始化

#include <iostream>
class Person {
private:
    std::string &name;

public:
    // 使用初始化列表进行 name 的初始化
    Person(std::string& name) : name(name) {}

    std::string getName() { return name; }
};

int main() {
    std::string myName = "Jack";
    Person p(myName);
    std::cout << p.getName() << std::endl; // 输出 Jack
    return 0;
}

5.3 成员变量是类或结构体类型

如果成员变量是类或结构体类型,则需要在构造函数中调用它们的构造函数来完成初始化。使用初始化列表可以直接调用构造函数来初始化这些成员变量,避免了在构造函数体中进行赋值操作的繁琐步骤。

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

class Circle {
private:
    double m_radius;
    Point m_center;
public:
    Circle(double radius,  Point& center): m_radius(radius), m_center(center) {}
};

int main() {
    Point center(0, 0);
    Circle c(1.0, center);
    return 0;
}

5.4 父类成员变量的初始化

如果一个派生类中包含父类的成员变量,那么可以通过初始化列表来初始化这些成员变量。

class Animal {
public:
    int age;
};

class Cat : public Animal {
public:
    string name;

    Cat(string n, int a) : Animal{a}, name{n} {}
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值