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