1.构造函数
1.1什么是构造函数
1.构造函数是一个非常特殊的函数,他的作用就是对类中的属性进行初始化的。
2.构造函数带void 都没有。
3.构造函数也是函数,也遵从C++函数重载机制。
1.2语法形式
函数名字与类型名字相同 + 函数形参列表
{
//对类中的属性进行初始化
}
1.3代码
1.3.1无参数代码
#include <iostream>
using namespace std;
class Stu{
private:
int age;
string name;
public:
void set_xinxi(int age,string name){
this->age=age;
this->name=name;
}
Stu(){
cout << "这是一个无参的空构造" << endl;
}
void show_info(){
cout << "name=" << this->name << ",age=" << this->age << endl;
}
};
int main()
{
Stu stu;
stu.set_xinxi(12,"peter");
stu.show_info();
return 0;
}
1.3.2有参数代码
#include <iostream>
using namespace std;
class Stu{
private:
int age;
string name;
public:
void set_xinxi(int age,string name){
this->age=age;
this->name=name;
}
Stu(int age,string name){
this->age=age;
this->name=name;
cout << "这是一个有参的构造" << endl;
}
void show_info(){
cout << "name=" << this->name << ",age=" << this->age << endl;
}
};
int main()
{
Stu stu(12,"lisi");
stu.show_info();
return 0;
}
1.4构造函数的调用方式
1.4.1显示调用
class Stu{
private:
int age;
string name;
public:
Stu(int age,string name){
this->age=age;
this->name=name;
cout << "这是一个有参的构造" << endl;
}
void show_info(){
cout << "name=" << this->name << ",age=" << this->age << endl;
}
};
int main()
{
Stu stu(12,"lisi");
stu.show_info();
return 0;
}
1.4.2隐式调用
#include <iostream>
using namespace std;
class Stu{
private:
int age;
public:
Stu(int age){
this->age=age;
cout << "这是一个有参的构造" << endl;
}
void show_info(){
cout << "age=" << this->age << endl;
}
};
int main()
{
Stu stu=12;
stu.show_info();
return 0;
}
1.4.3调用说明,因为一般来说隐式调用比较难理解一些所以大多数情况下还是用显示调用,explicit是用来修饰类中的构造函数,防止隐式调用,如下:
#include <iostream>
using namespace std;
class Stu{
private:
int age;
public:
explicit Stu(int age){
this->age=age;
cout << "这是一个有参的构造" << endl;
}
void show_info(){
cout << "age=" << this->age << endl;
}
};
int main()
{
Stu stu(12);
stu.show_info();
return 0;
}
1.5构造函数的调用时机
当使用带用带括号的构造来构造对象,编译器自动对类中属性进行初始化。(优化操作)
#include <iostream>
using namespace std;
class Stu{
private:
int age;
int a;
int b;
public:
void show_info(){
cout << this->age << this->a << this->b << endl;
}
};
int main()
{
Stu stu=Stu();
stu.show_info();
return 0;
}
2析构函数
2.1析构函数的意义:
用来回收类中属性成员属性指针指向堆区的资源的。
2.2语法格式
~类名()
{
//析构函数的函数体
//析构函数的函数中书写回收为类中有属性指针指堆区资源的逻辑。
}
2.3代码说明
#include <iostream>
using namespace std;
class Stu
{
int age;
string name;
int *p;
Stu(){
p=new int [1024] {0};
cout << "Stu的无参的构造" << endl;
}
~Stu(){
delete [] p;
cout << "Stu的析构" << endl;
}
};
int main()
{
return 0;
}
总结:
1.在堆区定义对象时,首先使用new开辟空间,然后调用类中与之相对应的构造函数,构造对象,构造对象的过程就是对象中的属性初始化的过程。
2.当定义的对象要被销毁时,使用delect或者delect[];首先调用析构函数,然后再释放。
3.特殊属性的初始化:
3.1const修饰的类中的属性常成员对象的初始化及没有默认构造的成员对象的初始化。
#include <iostream>
using namespace std;
class A{
public:
A(int num)
{
}
};
class Stu
{
private:
const int id;
int age;
string name;
int *p;
A a;
public:
Stu(int _id,int _age,string _name):id(_id),age(_age),name(_name),p(new int [1024]),a(A(1))
{
cout << "Stu的无参的构造" << endl;
}
~Stu()
{
delete [] p;
p=nullptr;
cout << "Stu的析构" << endl;
}
void show_info()
{
cout << this->age << this->name << this->id << endl;
}
};
int main()
{
Stu stu(1001,12,"lisi");
stu.show_info();
return 0;
}
3.2初始化注意的几点
1.每个成员变量在初始化列表中,只能初始化一次。
2.类中有特列成员时的初始化:const修饰的对象或变量,引用类型成员或变量,自定义类型指定调用构造函数时使用(特别是有explicit修饰的构造函数的类类型对象时)。
3.如果类中有比较大的结构体对象的初始始化可以入在初始化列表中进行。
4.初始化列表顺序尽量保持与类中成员声明顺序相同
3.3类中static修饰的初始化
我们先来看一看static修饰的成员变量具有那些特点:
当一个进程被加载时,系统会首先加载静态区数据,静态区的数据就包括了什么呢?
1.文件代码段:编译好的机器码,内容即是一个个函数机器码及保存函数地址的指针。
2.rodata段:即全局只读数据,比如:全局const修饰的全局变量或常量字符串。
3.data段:已初始化的全局变量或static修饰且初始化的变量。(data段数据将在程序执行前系统调用copy_data()函数向程序中拷贝一分副本)
4.bss段:未被初始化的全局变量,或局部定义使用static 修饰的静态变量。(bss段数据在程序执行前系统会调用bss_clear()函数对其进行初始化)
当用static在类中修饰一个成员变量时,由于类中定义的static是隐藏在类中,系统无法调用bss_clear()直接进行初始化。所有C++语法规定,当系统加载时,必须要在类外进static修饰的成员变量进行首次初始化。
如果没有在类外对static修饰的成员变量进行初始化,那么此变量在类中仅为一个声明,而没有定义,当对象引用这个成员变量时就会发生链接错误。所以如果类中的变量在类中有用static修饰,必须要在类外进行初始化。我们一般在main函数前或类的.cpp文件中进行初始化。
3.3代码
3.3.1代码实现
#include <iostream>
using namespace std;
class A{
public:
A(int num)
{
}
};
class Stu
{
private:
int age;
string name;
static int counts;
public:
Stu(int _age,string _name):age(_age),name(_name)
{
cout << "Stu的无参的构造" << endl;
counts++;
}
void show_info()
{
cout << &counts << endl;
}
};
int Stu::counts=0;
int main()
{
Stu stu(12,"lisi");
stu.show_info();
Stu stu1(13,"zhansan");
stu.show_info();
return 0;
}
#include <iostream>
#include <unistd.h>
using namespace std;
class A
{
public:
A(int score)
{
}
};
class Stu
{
private:
const int id;
string name;
int age;
int* p;
A a;
static int count;
public:
//类中的初始化列表的使用方式。
//即对类中的const修饰的属性进行初始化的,同时也可以对类中有自定义类类型却没有默认构造的对象,用来指定调用那一个构造来构造对象的。
//类中的初始化列表的初始化顺序,尽量保持与类中的属性的声明顺序相同。
Stu(int _id,string _name,int _age):id(_id),name(_name),age(_age),p(new int[1024]),a(A(1))
{
cout << "Stu的有参的构造" << endl;
count++;
}
~Stu()
{
//所以说析构函数:就是用回收类中属性指针指向堆区资源的。
delete []p;
cout << "Stu的析构" << endl;
}
void showInfo()
{
cout << "学号:" << this->id << ",姓名:" << this->name << ",年龄:" << this->age << endl;
}
int get_count()
{
cout << &count << endl;
return count;
}
};
//类外静态成员变量(属性)初始化方式:
int Stu::count = 0;
//static 在局部空间的使用:static修饰的局部变量只能被初始化一次。
void test()
{
while (true) {
static int i = 0;
i++;
sleep(1);
cout << i << endl;
}
}
int main()
{
Stu stu(1001,"zhangsan",18);
stu.showInfo();
cout << stu.get_count() << endl;
Stu stu1(1002,"lisi",20);
cout << stu.get_count() << endl;
//测试局部作用域的static修饰的变量。
test();
return 0;
}
#include <iostream>
using namespace std;
class Stu
{
private:
static int counts;
};
int Stu::counts=0;
int main()
{
cout << sizeof (Stu) << endl;
return 0;
}
3.3.2现象
3.3.3总结说明
1.由于类中静态成员变量,是属于整个进程的,进程执行前就已经在静态区存在了,而类对象是生存在动态区的,所以此成员不依赖于某个类对象的,他是属于整个进程的,因为他又隐藏于类中,所以我们可以使用类域访问的形式对直接访问。(当于有类内的访问权限的限制。)
2.static只能被初始化一次,下一次的值是在这个基础之上,进行变化的
3.static修饰的成员变量必须在类外进行初始化。
4.static修饰的成员变量不依赖于某个对象,可以直接使用类名 + 域名访问符的形式,直接访问。
5.static修饰的成员变量不占有类对象的空间,且此静态成员数据只有一分,被所有类对象共享。
6.所以当类中有需要定义一个为这个类而服务,而不是某个对象而服务的属性时,就可以把它升级static的成员属性。
7.由于静态成员变量定义在静态区定义内存,而对象是存在于动态区之中,所以静态成员变量并不占用类对象的内存空间。