C++对象特性
#构造函数 和 析构函数
构造函数:主要为对象属性赋值
语法:类名(){}
注意:
1.无返回值也无void
2.函数名称与类名相同
析构函数
语法:~类名(){}
注意:
1.无返回值也无void
2.不可以有参数,不可发生重载
class Person
{
public:
//构造函数
Person()
{
cout<<"Person 构造函数实例"<<"\n";
}
//析构函数
~Person()
{
cout<<"Person 析构函数实例"<<"\n";
}
};
构造函数的分类
按参数类型分:有参构造 和 无参构造
按类型分:普通构造 和 拷贝构造
调用方式:括号法 和 显式法 和 隐式转换法
#include<iostream>
using namespace std;
class Person
{
public:
//无参构造
Person()
{
cout<<"Person 构造函数实例"<<"\n";
}
//有参构造
Person(int a)
{
age = a;
cout<<"Person 有参构造实例"<<"\n";
}
//拷贝构造
Person(const Person &p)
{
age = p.age;//把对象的属性拷贝到自己身上
}
//析构函数
~Person()
{
cout<<"Person 析构函数实例"<<"\n";
}
int age = 10;
};
//调用
//括号法
//注意点:调用无参函数的时候不要加上小括号(),会被认为是一个函数的声明
//eg: Person p();
void test01()
{
Person p;
Person p2(10);//有参函数调用
Person p3(p2);//拷贝函数调用
}
//显式法
//注意:不要利用拷贝函数来初始化匿名对象
//下列函数赋值的右边就是 匿名对象
void test02()
{
Person p1;
Person p2 = Person(10);
Person p3 = Person(p2);
}
//隐式转换法
void test03()
{
Person p1;
Person p2 = 10;
Person p3 = p2;
//相当于编辑器自动转换为 Person p3 = Person(p2);
}
int main()
{
//test01();
//test02();
test03();
return 0;
}
#
拷贝构造函数调用时机
三种方式:
1.使用一个已经创建完毕的对象来初始化一个新对象
2.值传递的方式给函数参数传值
3.以值方式返回局部对象
#include<iostream>
using namespace std;
class Person
{
public:
Person()
{
cout<<"Person 默认函数构造实例"<<"\n";
}
Person(int age)
{
cout<<"Person 有参函数实例"<<"\n";
m_age = age;
}
Person(const Person &p)
{
cout<<"Person 拷贝函数实例";
}
~Person()
{
cout<<"Person 析构函数实例"<<"\n";
}
int m_age;
};
void test01()
{
Person p1(10);
Person p2(p1);
}
//值传递方式给函数参数传值
void doWork(Person p)
{
}
void test02()
{
Person p;
doWork(p);
}
//值方式返回局部对象
Person doWOrk()
{
Person p1;
return p1;
}
void test03()
{
Person p2 = doWOrk();
}
int main()
{
test03();
return 0;
}
#
深拷贝和浅拷贝
浅拷贝:是编译器自动对开的内存自动赋值的操作
但是面临一点就是你要是新开一段为堆区的空间,整个流程遵循栈的设计,后进先出,那么你就会先手动释放堆的空间,但是你原本默认构造里的空间就不可以再继续释放了!
解决方案:自己写一个拷贝函数(称为深拷贝)自己再开一个空间,为你自己写的堆区重新申请空间
#include<iostream>
#include<algorithm>
using namespace std;
class Person
{
public:
Person()
{
cout<<"Person 默认构造函数"<<"\n";
}
Person(int age,int height)
{
M_age = age;
M_Height = new int(height);
//Height = new int(height);//开辟一个在堆区的内存
cout<<"Person 有参构造函数"<<"\n";
}
//用深拷贝解决浅拷贝重复释放的问题
//再开一个地址给我自己开的堆区空间
Person(const Person &p)
{
cout<<"Person 拷贝函数实例"<<'\n';
M_age = p.M_age;
M_Height = new int(*p.M_Height);
}
~Person()
{
//注意:
//堆区开辟的内存空间需要手动进行释放
if(M_Height != NULL)
{
delete M_Height;
M_Height = NULL;
}
cout<<"Person 析构函数"<<"\n";
}
int M_age;
int *M_Height;
};
void test01()
{
Person p1(10,160);
cout<<"p1的年龄是"<<p1.M_age<<" "<<"p1的身高是"<<*p1.M_Height<<"\n";
Person p2(p1);
cout<<"p2的年龄是"<<p2.M_age<<" "<<"p1的身高是"<<*p2.M_Height<<"\n";
}
int main()
{
test01();
return 0;
}
#
初始化列表
语法:构造函数():属性1(值1),属性2(值2){}
#include<iostream>
using namespace std;
class Person
{
public:
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 p(40,40,40);
cout<<p.m_A<<"\n";
cout<<p.m_B<<"\n";
cout<<p.m_C<<"\n";
}
int main()
{
test01();
return 0;
}
#
类对象作为类成员
ps:
构造:先构造类对象,再构造自身
析构:先析构自身,再析构类对象
#include<iostream>
#include<string>
using namespace std;
//设计 手机 的类
class Phone
{
public:
Phone(string p_name)
{
ren_pinpai = p_name;
}
string ren_pinpai;
};
class Person
{
public:
Person(string name1,string pname):ren_name(name1),ren_phone(pname)
{
//ren_name = name1;
}
string ren_name;
Phone ren_phone;
};
void test01()
{
Person p("Alaso_shuang","Iphone");
cout<<p.ren_name<<" "<<p.ren_phone.ren_pinpai<<"\n";
}
int main()
{
test01();
return 0;
}
#
静态成员
静态成员变量
#include<iostream>
using namespace std;
class Person
{
public:
static int m_A;
};
int Person::m_A = 100;
void test01()
{
Person p;
cout<<p.m_A<<"\n";
Person p2;
p2.m_A = 200;
cout<<p2.m_A<<"\n";
}
void test02()
{
//通过对象进行访问
Person p1;
cout<<p1.m_A<<"\n";
//通过类名进行访问
cout<<Person::m_A<<"\n";
}
int main()
{
//test01();
test02();
return 0;
}
静态成员函数
#include<iostream>
using namespace std;
class Person
{
public:
static void f()
{
m_A = 100;//静态成员变量
cout<<"Static void f的调用"<<"\n";
}
static int m_A;
};
int Person::m_A = 0;
void test01()
{
//通过对象访问
Person p;
p.f();
//通过类名进行访问
Person::f();
}
int main()
{
test01();
return 0;
}
#
成员变量和成员函数分开存储
提要点:
1.只有非静态成员变量才属于类的对象且占用类的内存位置,显示字节大小
2.C++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占用内存位置
#include<iostream>
using namespace std;
class Person
{
public:
int m_A;
static int m_B;//静态成员变量
void f()
{
int k;
}
};
int Person::m_B;
void test01()
{
Person p;
cout<<sizeof(p)<<"\n";
}
//单纯一个空对象占用字节为 1
void test02()
{
Person p1;
cout<<sizeof(p1)<<"\n";
}
int main()
{
//test01();
test02();
return 0;
}
#
this指针
作用:
1.解决命名的冲突(建议规范命名,不要无形增加代码可阅读性)
2.返回对象本身(让他原本void 的函数不再返回void)
#include<iostream>
using namespace std;
class Person
{
public:
//解决名称冲突
Person(int age)
{
this->age = age;
//注意一般我们这里需要用不同变量去区分,但是这里只是极端情况去处理相同变量的情况
}
Person& PersonAdd(int age1)//取消引用的话仅次拷贝了一份数据,类似于平行宇宙
{
this->age1 += age1;
//返回对象本身
return *this;
}
int age = 20;
int age1 = 10;
};
void test01()
{
Person p(20);
cout<<p.age<<"\n";
}
void test02()
{
Person p1(20);
Person p2(10);
p2.PersonAdd(10).PersonAdd(10).PersonAdd(10);//可以利用this指针进行不断累加
cout<<p2.age1<<"\n";
}
int main()
{
//test01();
test02();
return 0;
}
#
空指针访问成员函数
提要点:
1.空指针能访问成员函数,但是如果需要将空指针中成员函数打印出来需要提前特判是否为空指针,要不然空指针打印就会直接报错
#include<iostream>
using namespace std;
class Person
{
public:
void showName()
{
cout<<"hello"<<"\n";
}
void showName1(int age)
{
if(this == NULL)return;
this->age = age;//传入了一个空指针,所有我们需要判断这个指针是否为空
cout<<this->age<<"\n";
}
int age;
};
void test01()
{
Person *p = NULL;
p->showName();
p->showName1(20);
}
int main()
{
test01();
return 0;
}
#
常函数和常对象
提要点:
1.常函数本质上就是一个this指针,每个函数都内含this指针(指针常量),不允许修改指针指向
2.常对象其实就是对象前面加一个const修饰,也表示该对象任何一个属性不可改变
3.常对象只能调用常函数,一般函数调用不起是因为其他函数能随便改变一个成员的值,那么这样来说就不符合常对象本质
#include<iostream>
using namespace std;
class Person
{
public:
Person()
{}
//常函数
void f()const //修饰的其实是this指针
{
this->m_B = 100;
//this->m_A = 100;//直接报错了,常函数不能修改值
}
int m_A;
mutable int m_B;
};
void test01()
{
Person p1;
p1.f();
}
//常对象
void test02()
{
const Person p;
//p.m_A = 100;//不允许修改
}
int main()
{
test02();
return 0;
}