构造函数语法:
主要作用于创建对象时为对象的成员变量属性赋值,构造函数由编译器自己调用,无需手动调用。
- 构造函数,没有返回值也不写void
- 函数名称与类名相同
- 构造函数可以有参数,因此可以发生重载
- 程序在调用对象时会自动调用构造函数,无须手动调用,而且只会调用一次
#include<iostream>
using namespace std;
/*
构造函数:
构造函数在对象创建时自动调用无需手动调用
当类内没有实现构造函数时,编译器会提供默认的构造函数(默认的构造函数什么也不做)
如果类内实现了任意一个构造函数编译器则不提供默认构造。
语法:没有返回值也不写void 函数名和类名相同 有函数参数可以重载
对于对象来说构造函数是初始化对象的,因为创建对象必须调用构造函数
对于成员变量来说构造函数是赋值的,因为先分配好内存,再调用构造函数依次赋值
*/
class Pointer {
int num;
int* p;
public:
Pointer() {
p = nullptr;
num = 0;
cout << "无参构造" << endl;
}
Pointer(int num) {
cout << " 有参构造" << endl;
this->num = num;
p = new int(num);
}
};
int main() {
//Pointer p1();不是调用无参构造,编译器会认为是函数声明 Pointer认为是函数的返回值 p1函数名字()函数参数
Pointer p1;//调用无参构造
Pointer p2(2);//调用有参构造
}
为什么构造函数会自动调用?
- 类的实例化:当你定义一个类的对象时,编译器会为这个对象分配内存
- 调用构造函数:在内存分配之后,编译器自动调用相应的构造函数以初始化对象。在这一步中,构造函数会被执行,任何需要的初始化工作都会在这里完成
- 没有手动调用:不需要显式地调用构造函数
#include <iostream>
class Person {
public:
// 默认构造函数
Person() {
std::cout << "使用默认构造函数创建的 Person" << std::endl;
}
// 带参数的构造函数
Person(const std::string& name) {
std::cout << "人员创建 " << name << std::endl;
}
};
class Team {
public:
// 构造函数,初始化成员
Team() {
std::cout << "Team created." << std::endl;
}
// 含有 Person 类型成员
Person member; // 将调用默认构造函数
};
int main() {
Team team; // 创建 Team 对象,随机调用默认构造函数
Person john("han"); // 显式调用带参数构造函数
return 0;
}
//输出
使用默认构造函数创建的 Person
Team created.
人员创建:han
构造函数地分类和调用:
两种分类方式:
按参数分为:有参构造和无参构造
按类型分为:普通构造和拷贝构造
三种调用方式:
括号法
显示法
隐式转换法
#include<iostream>
using namespace std;
class Pointer {
int num;
int *p;
public:
Pointer() {
p = nullptr;
num = 0;
cout << "无参构造" << endl;
}
/*explicit*/Pointer(int num) {
cout << "有参构造" << endl;
this->num = num;
p = new int(num);
}
};
int main() {
//括号法调用构造函数
Pointer p(1);
Pointer p1;
//显示法调用构造函数
Pointer p2 = Pointer();
Pointer p3 = Pointer(2);
//隐式法调用构造函数
Pointer p3 = 3;
return 0;
}
构造函数调用规则
默认情况下C++编译器至少给每一个类添加3个函数
- 默认构造函数
- 默认析构函数
- 默认拷贝构造函数,堆属性进行值拷贝
1. 用户自定义构造函数的影响
1.1 有参构造函数和默认构造函数
如果用户自定义有参构造函数,C++不再提供默认构造函数,但是会提供默认拷贝构造
#include <iostream>
class MyClass {
public:
MyClass(int x) { // 有参构造函数
std::cout << "Constructed with value: " << x << std::endl;
}
};
int main() {
// MyClass obj; // 错误:没有默认构造函数
MyClass obj(42); // 正确使用有参构造函数
return 0;
}
//输出
Constructed with value: 42
1.2 拷贝构造函数的自动生成
-
规则:如果用户定义了任何构造函数(包括有参构造函数),C++仍然会生成一个默认的拷贝构造函数,但仅在用户没有提供自己的拷贝构造函数的情况下。
class MyClass {
public:
MyClass(int x) : value(x) {} // 用户定义的有参构造函数
// 默认的拷贝构造函数会被隐式提供
// MyClass(const MyClass& other) = default;
void display() const {
std::cout << "Value: " << value << std::endl;
}
private:
int value;
};
int main() {
MyClass obj1(42); // 使用有参构造函数
MyClass obj2 = obj1; // 自动调用生成的拷贝构造函数
obj2.display(); // 输出 "Value: 42"
return 0;
}
//输出
Value: 42
2. 用户自定义构造函数的规则
2.1 自定义拷贝构造函数
如果用户定义了一个拷贝构造函数,C++将不再提供默认无参构造和默认的拷贝构造函数
class MyClass {
public:
MyClass(int x) : value(x) {} // 有参构造函数
// 自定义拷贝构造函数
MyClass(const MyClass& other) {
std::cout << "Copy constructor called!" << std::endl;
value = other.value;
}
// 如果没有定义下面的构造函数,编译器将不再提供
// MyClass() = default;
void display() const {
std::cout << "Value: " << value << std::endl;
}
private:
int value;
};
int main() {
MyClass obj1(42); // 使用有参构造函数
MyClass obj2(obj1); // 使用自定义拷贝构造函数
obj2.display(); // 输出 "Value: 42"
// MyClass obj3; // 错误:没有默认构造函数
return 0;
}
//输出
Copy constructor called!
Value: 42
重要总结
- 没有默认构造函数:当你定义了任何构造函数(如有参构造函数),C++不会自动提供默认(无参数)构造函数。
- 拷贝构造函数:如果定义了拷贝构造函数,C++将不提供其他构造函数(包括默认构造函数),即使用户自己没有定义自己的构造函数。
- 自定义构造函数对类行为的影响:用户的定义会直接影响类的可用性,特别是在如何创建对象、复制对象等方面。
额外注意事项
- 构造函数的定义优先级:用户可以自定义多个构造函数来满足不同的需求,但是保证其逻辑正确并避免不必要的复制是非常重要的。
- 显式删除:C++11引入了
delete
,可以显式删除构造函数,进一步控制对象的创建与复制。
析构函数
析构函数语法:
- 析构函数,没用返回值也不写void
- 函数名称与类名相同,在名称前加上符号~
- 析构函数不可以有参数,因此不可以发生重载
- 程序在对象销毁前会自动调用析构函数无须手动调用且只会调用一次
示例:
#include<iostream>
using namespace std;
/*
析构函数:
对象被释放时自动调用析构函数,析构函数是用来是否成员变量指向的堆区内存的
语法:~类名(){},析构函数没有参数
*/
class A {
int num;
int* p;
public:
A() {
num = 0;
p = nullptr;
}
A(int num) {
this->num = num;
p = new int(num);
}
~A() {
if (p)delete p;
cout << "析构函数";
}
};
int main() {
A a(3);
A* a1 = new A(3);
delete a1;
return 0;
}