一、.初始化:
对象的初始化使用构造函数
构造函数
构造函数:主要作用在于创造对时为对象的成员属性(成员变量)赋值,构造函数由编译器自动调用,无需手动调用
语法:类名(){}
1.构造函数没有返回值,函数名前也不用写void
2.函数名与类名相同
3.构造函数可以有参数,因此可以发生重载
4.程序在调用对象的时候会自动调用构造函数,无需手动调用,而且只会调用一次
5.当已经提供了有参构造时,程序不会提供无参构造(若此时调用无参构造,程序报错,须程序员手动提供无参构造)
#include<iostream>
#include<iomanip>
#include<cmath>
using namespace std;
class A {
int* p;
public:
A()//构造函数,函数名与类名相同,没有返回值,函数名前不用写void,
//如果没有实现构造函数编译器会提供默认的构造函数
{
cout << "默认构造" << endl;
}
A(int n) {
if (n != 0) {
p = new int[n];
cout << "使用构造函数在堆区申请了n个int类型的变量" << endl;
}
}
};
int main() {
A p;//输出:默认构造。在创建对象时编译器会帮我们自动调用构造函数,无需手动调用
A p1(2);//输出:使用构造函数在堆区申请了n个int类型的变量。调用有参构造创造对象
A p2();//输出: 编译器会把它认为是函数声明,A是返回值类型,p2是函数名,()是函数参数
return 0;
}
构造函数的分类及调用:
分类:
1.按照参数分:有参构造和无参构造
2.按照类型分:普通构造和拷贝构造
调用方式:
1.括号发
2.显示法
3.隐式转换法
#include<iostream>
#include<iomanip>
#include<cmath>
using namespace std;
class A {
int* p;
public:
A()
{
cout << "默认构造" << endl;
}
A(int n) {
cout << "调用有参构造" << endl;
if (n > 0)p = new int(n);//p指向一块int类型的内存,值为n
else{
p = NULL;
cout << "数据不准确" << endl;
}
}
void a(){
if (p) {
cout << "打印p指向堆区内存值:" << *p << endl;
}
}
};
int main() {
A t(1);//输出:调用有参构造。括号法相当于A t=A(1);
A t1 = A(3);//输出:调用有参构造。显示法
A t2 = 3;//输出:调用有参构造。隐式转换法,在此处会调用参数为int类型的构造函数,将3转换为A类型对象
A t3();//不是括号发,编译器会把它认为是函数声明,A是返回值类型.t3是函数名,()是函数参数
A t4;//输出:默认构造。调用无参构造
return 0;
}
构造函数的调用规则
在默认情况下,C++至少会给一个类添加3个函数:
1.默认构造函数(无参,函数体为空)
2.默认析构函数(无参,函数体为空)
3.默认拷贝构造函数,对属性进行值拷贝。(简单的赋值)
构造函数调用规则:
1.如果用户定义有参构造,C++不再提供默认的无参构造,但会提供默认的拷贝构造
2.如果用户定义了拷贝构造,C++不再提供其他的任何构造函数(无参、有参、默认拷贝构造)
#include<iostream>
#include<vector>
using namespace std;
class Person {
public:
int n;
Person() {
cout << "无参构造" << endl;
}
Person(int n) {
this->n = n;
cout << " 有参构造" << endl;
}
Person(const Person& other) {
this->n = other.n;
cout << " 拷贝构造" << endl;
}
~Person() {
cout << "析构函数" << endl;
}
};
void test1() {
Person p1(18);
Person p2(p1);//如果不写拷贝构造,编译器会自动添加默认的拷贝构造(浅拷贝)
//此处的拷贝构造,相当于使用一个已经创建完毕的对象(已经存在的对象)初始化一个新对象
cout << "p2为:" << p2.n << endl;
}
void test2() {
//如果用户提供有参构造,编译器不会提供默认的无参构造,但会提供默认的拷贝构造
Person p1;//此时如果用户没有提供默认构造,编译器会报错
Person p2(10);//调用用户提供的有参构造
Person p3(p2);//调用拷贝构造,如果此时用户没有提供拷贝构造,编译器会提供默认的拷贝构造
//如果用户提供了拷贝构造,编译器不会提供其他任何的拷贝构造
Person p4;//此时如果用户没有提供默认构造,编译器会报错
Person p5(10);//调用用户提供的有参构造,此时如果用户没有提供有参构造,编译器会报错
Person p6(p5);//调用拷贝构造
}
int main() {
test1();
return 0;
}
二、对象的清理
对象需要调用析构函数来清理
析构函数
主要作用在于对象销毁前系统自动调用,执行一些清理工作
语法:~( )类名{ }
1.析构函数没有返回值,也不用写void
2.函数名跟类名相同,在名称前加符号:~
3.析构函数不可以有参数,因此不能发生重载
4.程序在对象销毁前会自动调用析构,无需手动调用,而且只会调用一次
#include<iostream>
using namespace std;
class Pointer {
int* p=NULL;
public:
Pointer() {
cout << "默认构造" << endl;
}
Pointer(int n) {
cout << "调用有参构造" << endl;
if (n > 0) {
p = new int[n];//使用构造函数在堆区申请了n个int类型的变量
}
}
//析构函数用于帮助我们释放指针变量指向的堆区内存
~Pointer() {
if (p)delete[]p;
}
};
void test() {
Pointer p(2);
}
int main() {
test();
return 0;
}