构造函数
构造函数作用
在创建一个新的对象时,自动调用的函数,用来进行“初始化”工作:对这个对象内部的数据成员进行初始化。
构造函数特点
1.自动调用(在创建新对象时,自动调用)
2.构造函数的函数名,和类名相同
3.构造函数没有返回类型
4.可以有多个构造函数(即函数重载形式)
构造函数种类
默认构造函数
自定义的构造函数
拷贝构造函数
赋值构造函数
默认构造函数
没有参数的构造函数,称为默认构造函数。
合成的默认构造函数
但没有手动定义默认构造函数时,编译器自动为这个类定义一个构造函数
1)如果数据成员使用了“类内初始值”,就使用这个值来初始化数据成员。
2)否则,就使用默认初始化(实际上,不做任何初始化)
#include <iostream>
using namespace std;class Human {
public:Human(){cout << "Human constructor called." << endl;}
};
int main() {Human p1;return 0;
}
只要手动定义了任何一个构造函数,编译器就不会生成“合成的默认构造函数”一般情况下,都应该定义自己的构造函数,不要使用“合成的默认构造函数”【仅当数据成员全部使用了“类内初始值”,才宜使用“合成的默认构造函数”】
赋值构造函数
#include <iostream>
using namespace std;class Person {
public:void setAddr(char* p) {addr = p;}void description() const {cout << "Address: " << addr << endl;}
private:char* addr;
};int main() {char* str = new char[100] {"123 Main St"};Person p1;p1.setAddr(str);Person p2;p2 = p1; // 执行浅拷贝p1.description(); // 应该打印: Address: 123 Main Stp2.description(); // 应该也打印: Address: 123 Main St// 修改原始字符串,并查看 p1 和 p2 是否受到影响strncpy_s(str,100 , "456 Elm St" , _TRUNCATE );p1.description(); // 现在打印: Address: 456 Elm Stp2.description(); // 也打印: Address: 456 Elm Stdelete[] str; // 记得删除动态分配的内存return 0;
}
你执行 p2 = p1;
这一行时,默认情况下编译器会使用浅拷贝(shallow copy)。这意味着如果你的类中有指针或者持有对其他资源的引用的话,那么这些资源会被同样的引用到新的对象中去,而不是创建独立的副本。
我们可以自定义运算符重载Person& operator=(const Person& person)来实现深拷贝
#include <iostream>
using namespace std;class Person {
public:void setAddr(char* p) {addr = p;}void description() const {cout << "Address: " << addr << endl;}Person& operator=(const Person& p) {//防止对象自身赋值if (this == &p) return *this;//如果执行f2 = f1//就会调用f2.operator=(f1)//如果有必要 先释放自己的动态资源delete[] addr;//再拷贝p的动态资源到自己 addr = new char[strlen(p.addr) + 1];strcpy_s(addr, strlen(p.addr) + 1, p.addr);//返回对象本身的引用,是为了方便能够链式处理//f1 = f2 = f3return *this;}~Person() {delete[] addr;}
private:char* addr;
};int main() {char* str = new char[100] {"123 Main St"};Person p1;p1.setAddr(str);Person p2;p2 = p1; // 执行浅拷贝p1.description(); // 应该打印: Address: 123 Main Stp2.description(); // 应该也打印: Address: 123 Main St// 修改原始字符串,并查看 p1 和 p2 是否受到影响strncpy_s(str,100 , "456 Elm St" , _TRUNCATE );p1.description(); // 现在打印: Address: 456 Elm Stp2.description(); // 也打印: Address: 456 Elm Stdelete[] str; // 记得删除动态分配的内存return 0;
}
拷贝构造函数
#include <iostream>
using namespace std;class Person {
public:Person() {age = 0;name = "";}Person(const Person& p) {cout << "Copy constructor called." << endl;age = p.age;name = p.name;}void setAge(int a) {age = a;}void setName(string n) {name = n;}
private:int age;string name;
};int main() {Person p1;p1.setAge(25);p1.setName("John");Person p2(p1);Person p3 = p1;return 0;
}
合成的拷贝构造函数
是指当类中没有显式定义拷贝构造函数时,编译器自动为该类生成的一个默认拷贝构造函数。这个自动生成的拷贝构造函数会执行成员变量的浅拷贝(shallow copy),即将源对象的每个成员变量的值直接复制到新创建的对象中。 简单来说,合并的拷贝构造函数是编译器在类定义中没有显式提供拷贝构造函数时自动提供的一个默认实现。这个默认实现会逐成员地复制源对象的值到新对象中,但它不会处理动态分配的内存(如指针指向的内存)的深拷贝(deep copy)问题,这可能会导致资源泄露或双重释放等问题。
合成的拷贝构造函数都是浅拷贝 下面给出一个浅拷贝的例子。
#include <iostream>
using namespace std;class Person {
public:void mallocAdress() {address = new char[100];}~Person() {if(address) delete[] address;}void print() {printf("address: %p\n", address);}
private:char* address = nullptr;
};int main() {Person p1;p1.mallocAdress();Person p2 = p1;Person p3(p1);p1.print();p2.print();return 0;
}
要解决合并的拷贝函数,我们可以自己定义一个拷贝构造函数
#include <iostream>
using namespace std;class Person {
public:void mallocAdress() {address = new char[100];}~Person() {if(address) delete[] address;}void print() {printf("address: %p\n", address);}Person(const Person& p) {address = new char[100];}
private:char* address = nullptr;
};int main() {Person p1;p1.mallocAdress();Person p2 = p1;Person p3(p1);p1.print();p2.print();return 0;
}
什么时候调用拷贝构造函数
1.调用函数时,实参是对象,形参不是引用类型
#include <iostream>
using namespace std;class MyClass {
public:MyClass(int val) {this->val = val;cout << "Constructor called" << endl;}MyClass(const MyClass& obj) {cout << "Copy constructor called" << endl;this->val = obj.val;}int val;
};void func(MyClass obj) {cout << "Value of obj is " << obj.val << endl;
}int main() {MyClass obj1(10);func(obj1);return 0;
}
2.函数的返回类型是类,而且不是引用类型
#include <iostream>class MyClass {
public:MyClass(int val) : value(val) {std::cout << "MyClass constructor called." << std::endl;}MyClass(const MyClass& other) : value(other.value) {std::cout << "Copy constructor called." << std::endl;}int value;
};
MyClass returnObject() {MyClass temp(20);return temp; // 返回局部变量会调用拷贝构造函数
}
int main() {MyClass returned = returnObject(); // 这里也会调用拷贝构造函数std::cout << "Returned object value: " << returned.value << std::endl;return 0;
}
虽然会因为RVO优化,不调用MyClass的拷贝构造函数
3.对象数组的初始化列表中,使用对象。
#include <iostream>class MyClass {
public:MyClass(int val) : value(val) {std::cout << "MyClass constructor called." << std::endl;}MyClass(const MyClass& other) : value(other.value) {std::cout << "Copy constructor called." << std::endl;}int value;
};int main() {MyClass source(30);MyClass array[3] = { source, source, source }; // 这里会三次调用拷贝构造函数for (int i = 0; i < 3; ++i) {std::cout << "Array[" << i << "] value: " << array[i].value << std::endl;}return 0;
}
类
类和基本数据不同(int , float , double , long , long long)
类的构成:方法和数据
#include <iostream>
using namespace std;
class Human {
public:int age;string name;void print() {cout << "Name: " << name << ", Age: " << age << endl;}Human() {cout << "Constructor called." << endl;}Human(const Human& h) {cout << "Copy constructor called." << endl;}Human& operator=(const Human& h) {cout << "Assignment operator called." << endl;return *this;}~Human() {cout << "Destructor called." << endl;}
};
int main() {Human h1;cout << "---" << endl;Human* h2 = &h1;return 0;
}
上面以指针的形式来进行初始化的话不会调用拷贝
如果某数据成员使用类内初始值,同时又在构造函数中进行了初始化,
那么以构造函数中的初始化为准。
相当于构造函数中的初始化,会覆盖对应的类内初始值
#include <iostream>class MyClass {
public:MyClass() : value(10) { // 类内初始化std::cout << "Default constructor called." << std::endl;}MyClass(int val) : value(val) { // 构造函数初始化std::cout << "Constructor with initial value called." << std::endl;}MyClass(const MyClass& other) : value(other.value) { // 拷贝构造函数std::cout << "Copy constructor called." << std::endl;}void printValue() const {std::cout << "Value is: " << value << std::endl;}private:int value = 5; // 类内初始值
};int main() {MyClass obj1; // 使用类内默认构造函数obj1.printValue(); // 输出 Value is: 10MyClass obj2(20); // 使用构造函数初始化obj2.printValue(); // 输出 Value is: 20return 0;
}