前言
本文主要是通过简单的示例,去学习与理解C++类的构造方法
构造方法的作用
为什么存在构造方法?为什么需要构造方法?
那是因为当我们在代码中定义一类变量(实例化一个类的实例/对象时)编译会帮我们申请对象所需要的内存(编译会给我们定义类生成默认的无参构造方法、析构方法、拷贝构造方法以及赋值构造方法)然后会根据类变量的使用情况(具体的代码语法与使用的上下文)调用对应的构造方法给对象分配内存和初始化类的成员变量,当我们有"扩展"的需求时就需要自定义有参型的构造方法和重写/覆盖无参构造方法、析构方法、拷贝方法方法等等。
构造方法的类型
构造方法的分类
- 默认构造方法/无参构造方法
- 普通有参构造方法(有两个以上参数的)(即除了转化构造、拷贝构造方法外的有参构造方法)
- 转换构造方法(有而只有一个参数)
- 拷贝构造方法(支持重载,即可以有多个)
- 赋值构造方法(严格来说不算构造方法,它是赋值运行符重载)
所有的构造方法的方法名是类名,且没有返回值(赋值构造方法除外)
接下具体看下各个构造方法的使用情况与相关的知识点
默认构造方法
- 默认构造方法是无参,无返回值
- 默认构造方法编译器会帮生成一下,如果我们没有定义的话
- 如果定义了其它的构造方法,编译器就不会帮忙生成默认的,这时候需要手动添加
class Person {
private:
int age;
public:
// Person() {
// cout << "无参构造方法" << endl;
// };
//定义了拷贝构造方法
Person(const Person& p) {
cout << "拷贝构造方法, age = " << age << endl;
this->age = p.age;
}
~Person() {
cout << "析构方法, age = " << age << endl;
}
int getAge() {
return this->age;
}
void setAge(int age) {
this->age = age;
}
};
void printPerson(Person p) {
cout << "Person, age = " << p.getAge() << endl;
}
Person retPerson() {
Person a;
/ **
这里会报错,clion下,clang编译报错,
No matching constructor for initialization of 'Person' candidate constructor not viable: requires single argument 'p', but no arguments were provided
*/
a.setAge(20);
return a;
}
普通有参构造方法
- 这里的有参是指有形参
- 方法命是类名,有两个以上的参数(一个的话就是类型转化构造方法了)
- 支持重载,即可以有很多个普通有参构造方法
#include <iostream>
using namespace std;
class Person {
private:
int age;
bool isVip;
float height;
public:
Person() {
cout << "无参构造方法" << endl;
};
Person(int age, bool isVip) {
cout << "有参构造方法1" << endl;
this->age = age;
this->isVip = isVip;
}
Person(int age, float height) {
cout << "有参构造方法2" << endl;
this->age = age;
this->height = height;
}
Person(const Person &p) {
cout << "拷贝构造方法, age = " << age << endl;
this->age = p.age;
}
~Person() {
cout << "析构方法, age = " << age << endl;
}
int getAge() {
return this->age;
}
void setAge(int age) {
this->age = age;
}
};
void printPerson(Person p) {
cout << "Person, age = " << p.getAge() << endl;
}
Person retPerson() {
Person a;
a.setAge(20);
return a;
}
int main() {
{
Person a(10, 170.0f);
Person b(18, true);
}
return 0;
}
类型转化构造方法
- 有且只有一个参数(参数类型不能是本身类)
- 在发生隐式类型转换时被调用
#include <iostream>
using namespace std;
class Person {
private:
int age;
bool isVip;
float height;
public:
Person() {
cout << "无参构造方法" << endl;
};
Person(int age) {
cout << "转换构造方法1" << endl;
this->age = age;
}
~Person() {
cout << "析构方法, age = " << age << endl;
}
};
void printPerson(Person p) {
cout << "Person, age = " << p.getAge() << endl;
}
int main() {
{
//printPerson的形参数是Persion类型,但由于定义了Person(int age),固能支持12到Persion的隐式类型转化
printPerson(12);
}
return 0;
}
代码运行结果如下
拷贝构造方法
- 默认编译器会帮生成一个
- 被拷贝的对象的形参引用必须带const修饰,即是一个常量引用
- 除拷贝的对象的形参外,还可以有其它的形,但需要有默认值
- 复制本类的一个实例
- 函数形参为一个对象,还有方法的形参类的值传递时,还有方法返回值是对象(这种情况现代编译器已经优化过不会再调用次拷贝构造方法了)时会调用到该方法
#include <iostream>
using namespace std;
class Person {
private:
int age;
bool isVip;
float height;
public:
Person() {
cout << "无参构造方法" << endl;
};
Person(const Person &p) {
cout << "拷贝构造方法, age = " << age << endl;
this->age = p.age;
}
// ~Person() {
// cout << "析构方法, age = " << age << endl;
// }
int getAge() {
return this->age;
}
void setAge(int age) {
this->age = age;
}
};
void printPerson(Person p) {
cout << "pint Person, age = " << p.getAge() << endl;
}
Person retPerson() {
Person a;
a.setAge(20);
return a;
}
int main() {
{
cout << ">> case1, 方法返回对象" << endl;
Person a = retPerson();
}
{
cout << ">> case2,形参对象" << endl;
Person a;
a.setAge(18);
printPerson(a);
}
{
cout << ">>case3, 显示调用拷贝构造" << endl;
Person a;
a.setAge(20);
Person b(a);
}
return 0;
}
代码运行结果如下
赋值构造方法
- 默认编译器会帮生成一个
- 被拷贝的对象的形参引用必须带const修饰,即形参是一个常量引用
- 是对=号操作符的重载,有返回值且是类的引用类型,所以是赋值操作的时候被调用
#include <iostream>
using namespace std;
class Person {
private:
int age;
bool isVip;
float height;
public:
Person() {
cout << "无参构造方法" << endl;
};
Person(const Person &p) {
cout << "拷贝构造方法, age = " << age << endl;
this->age = p.age;
}
Person& operator=(const Person &p) {
this->age = p.age;
cout << "赋值构造方法, age = " << age << endl;
return *this;
}
int getAge() {
return this->age;
}
void setAge(int age) {
this->age = age;
}
};
Person retPerson() {
Person a;
a.setAge(20);
return a;
}
int main() {
{
Person a;
a.setAge(20);
Person b;
b = a; //这里是真正的赋值操作, Person b = a; 这种写法是定义类变量,调用是拷贝构造方法
}
return 0;
}
代码运行结果如下