一、构造函数:对对象成员属性做初始化。
1、语法:类名 (){ }
- 没有返回值也不写void ;
- 函数名称与类名相同;
- 构造函数可以有参数,因此可以发生重载(函数名称相同,返回类型相同,参数不同,参数个数不同);
- 程序在调用对象时候会自动调用构造函数,无须手动调用,而且只会调用一次。
class Person
{
public:
Person()//普通构造
{
cout<<"构造函数的调用"<<endl;
}
};
void test01()
{
Person p;
}
int main(){
test01();
return 0;
}
2、两种分类方式:
1、按参数分:有参构造和无参构造(默认构造);
2、按类型分:普通构造和拷贝构造
//有参构造函数
class Person(){
public:
Person(int a)
{
age=a;
cout<<"person的有参构造函数调用"<<endl;
}
//拷贝构造函数
Person(const Person &p)
{
//将传入的人身上的所有属性,拷贝到我身上
age=p.age;
cout<<"Person的拷贝构造函数的调用"<<endl;
}
};
3、三种调用方法:
(1)、括号法
注意:调用默认构造函数的时候不加(),不能写成Person p1();会被编译器认为是函数的声明,不会认为在创建对象
(2)、显示法、
注意:单独的Person (10);被认为是匿名对象 特点是:当前执行结束后,系统立即会收掉匿名对象
注意:不要利用拷贝构造函数初始化匿名对象
例: Person(p3);
编译器会认为 Person (p3)==Person p3;是对象的声明。
(3)、隐式转换法
void test01()
{
//1、括号法
Person p1;//默认构造函数调用
Person p2(10);//调用有参构造函数
Person p3(p2);//拷贝构造函数的调用
//2、显示法
person p1;//默认构造
Person p2=Person(10);//有参构造
Peerson p3=Person (p2);//拷贝构造
//3、隐式转换法
Person p4=10;//相当于 写了 Person p4= Person (10); 有参构造
Person p5=p4;//拷贝构造
}
int main()
{
test01();
return 0;
}
4、拷贝构造函数调用时机
(1)、使用一个已经创建完毕的对象来初始化一个新对象
(2)、值传递的方式给函数参数传值
(3)、以值方式返回局部对象
二、析构函数:对对象销毁前系统自动调用,做清理工作。
1、语法:~类名 (){ }
- 没有返回值,也不写void ;
- 函数名称与类名相同,在名称前加上符号~ ;
- 析构函数不可以有参数,不可以发生重载;
- 对象在销毁前会自动调用析构函数,而且只会调用一次。
class Person{
public:
~Person()
{
cout<<"析构函数的调用"<<endl;
}
};
void test01()
{
Person p;
}
int main()
{
test01();
return 0;
}
编译器会强制要求实现对对象成员的初始化和清理,一般未写明时,编译器会自动生成自动调用,但内容为空,即空实现。
三、构造函数调用规则
默认情况下编译器会给一个类配置三个函数:
- 构造函数,无参,函数体为空。
- 析构函数,无参,函数体为空。
- 拷贝构造函数,对属性进行值拷贝。
三个函数的调用规则:
- 若用户定义有参构造函数,c++ 不再提供默认无参构造,会提供拷贝构造。
- 若用户定义拷贝构造函数,c++不会再提供其他构造函数。
四、深拷贝与浅拷贝
浅拷贝:简单的赋值拷贝操作,编译器提供的拷贝构造函数里提供的操作都是浅拷贝
存在问题:如果有数据开辟在堆区,在析构函数调用时,同一块堆区的内存重复释放
解决办法:深拷贝,自己写一个拷贝构造函数
深拷贝:在堆区重新申请一块空间,做赋值操作。
大概白话理解,就是在创建对象参数的时候有些指针类型的属性创建在堆区,在有对象进行拷贝操作的时候,如果调用的时编译器自带的拷贝构造函数,就会导致两个对象的属性指向同一块内存空间,在析构释放时,拷贝对象(P2)会先释放掉指向的那块内存空间,被拷贝对象(p1)在析构释放时会要求再释放一遍,这就导致了同一块内存空间重复释放的问题,解决办法就是自己写一个拷贝构造函数,在自己写的拷贝构造函数里,把指针类型的属性数据重新开辟一块空间(深拷贝)。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Person
{
public:
//有参构造函数
Person(int age, int height)
{
cout << "Person的有参构造函数调用" << endl;
m_age = age;
m_Height = new int (height);
}
//拷贝构造函数
Person(const Person& p)
{
cout << "Person的拷贝构造函数调用" << endl;
m_age = p.m_age;
//m_Height = p.m_Height;由编译器提供,默认实现的浅拷贝
m_Height = new int(*p.m_Height);//深拷贝,在堆区开辟一块新的空间,避免重复释放
}
//析构函数
~Person()
{
if (m_Height != NULL)
{
delete m_Height;
m_Height = NULL;
}
cout << "Person的析构函数调用" << endl;
}
int m_age;
int* m_Height;
};
void test01()
{
Person p1(18,158);
Person p2(p1);
}
int main()
{
test01();
return 0;
}
五、初始化列表
对类中的属性进行初始化操作。主要在构造函数中。
语法:类名():属性1(值1),属性2(值2)........{ }
示例:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Person
{
public:
//初始化列表①
Person() :m_A(10), m_B(20), m_C(30)
{
cout << m_A << endl;
cout << m_B << endl;
cout << m_C << endl;
}
//传统初始化方式②
Person(int a, int b, int c)
{
m_A = a;
m_B = b;
m_C = c;
}
//优化后的初始化列表操作③
/*Person(int a, int b, int c) :m_A(a), m_B(b), m_C(c)
{
}*/
int m_A;
int m_B;
int m_C;
};
void test01()
{ //②
Person p1(10, 20, 30);
cout <<p1.m_A << endl;
cout << p1.m_B << endl;
cout << p1.m_C << endl;
//①
Person p2;
cout << p2.m_A << endl;
cout << p2.m_B << endl;
cout << p2.m_C << endl;
//③
/*Person p3(100, 200, 300);
cout << p3.m_A << endl;
cout << p3.m_B << endl;
cout << p3.m_C << endl;*/
}
int main()
{
test01();
return 0;
}
六、类对象作为类成员
B类中有对象A作成员,A为对象成员。
会先构造对象成员A的对象,再构造自身B;
析构相反,会先析构自身B,再析构对象成员A。
七、静态成员
静态成员就是在成员变量和成员函数前加上关键字stastic,称为静态成员。
分类:
-
静态成员变量:
- 所有对象共享同一份数据
- 在编译阶段分配内存空间
- 类内声明,类外初始化
-
静态成员函数:
- 所有函数对象共享同一个函数
- 静态成员函数只能访问静态成员变量
例题:寻找并输出11~999之间的数m,它满足m、m²和m³均为回文数。
——回文数:各位数字左右对称的整数。
——11满足上述条件:11²=121,11³=1331.
分析:模10取余的方法,从最低位开始,依次取出该数的各位数字。按反序重新构成新的数,比较与原数是否相等,若相等,则原数为回文。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
bool func(unsigned n)
{
unsigned i = n;
unsigned m = 0;
while (i > 0)
{
m = m * 10 + i % 10;
i /= 10;
}
return m == n;
}
int main()
{
unsigned m = 11;
for (m = 11; m < 1000; m++)
{
if (func(m) && func(m * m) && func(m * m * m))
{
cout << "m=" << m << endl;
cout << "m*m=" << m * m<<endl;
cout << "m*m*m=" << m * m*m<<endl;
}
}
return 0;
}
运行结果: