C++入门基础教程:类和对象(中)
Posted Zhi Zhao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++入门基础教程:类和对象(中)相关的知识,希望对你有一定的参考价值。
前言
博主通过对C++基础知识的总结,有望写出深入浅出的C++基础教程专栏,并分享给大家阅读,今后的一段时间我将持续更新C++入门系列博文,想学习C++的朋友可以关注我,希望大家有所收获。
2 对象特性
续接上一节:C++入门基础教程(四):类和对象(上)
2.5 深拷贝与浅拷贝
浅拷贝:简单的复制拷贝操作(由编译器提供),易导致堆区的内存重复释放;
#include <iostream>
using namespace std;
class Person
{
public:
Person(int age,int height) // 有参构造函数
{
m_Age = age;
m_Height = new int(height); // 堆区开辟的数据,需要手动释放
cout << "Person有参构造函数的调用" << endl;
}
~Person() // 析构函数
{
// 将堆区开辟的数据做释放操作
if (m_Height != NULL)
{
delete m_Height;
m_Height = NULL;
}
cout << "Person析构函数的调用" << endl;
}
int m_Age; // 年龄
int *m_Height; // 身高
};
void test()
{
Person p1(18, 170);
cout << "p1的年龄为:" << p1.m_Age <<
" 身高:" << *p1.m_Height << endl;
Person p2(p1); // 会调用编译器提供的拷贝构造函数
cout << "p2的年龄为:" << p2.m_Age <<
" 身高:" << *p2.m_Height << endl;
}
int main()
{
test();
system("pause");
return 0;
}
执行语句 Person p2(p1); 会调用编译器提供的拷贝构造函数,该函数做的是浅拷贝,即将p1这个对象的属性复制一份给p2。由于new int(height) 的数据会存放在堆区,需要程序员手动释放,因此在上述析构函数中做数据的释放操作时,先释放p2的指针m_Height指向的内存,再释放p1的指针m_Height指向的内存(p1和p2是局部变量,存放在栈区,遵循后进先出的原则),但是这两个对象的指针m_Height指向的是同一块内存地址,这样就导致堆区的内存重复释放,程序不能成功运行。
深拷贝:在堆区重新申请空间,进行拷贝操作。
做深拷贝操作,此时p1和p2的指针m_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);
}
2.6 初始化列表:给变量赋初值
语法:构造函数():属性1(值1),属性2(值2),...{}
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 test()
{
Person p(13,34,18);
cout << "m_A= " << p.m_A << endl;
cout << "m_B= " << p.m_B << endl;
cout << "m_C= " << p.m_C << endl;
}
int main()
{
test();
system("pause");
return 0;
}
2.7 类对象作为类成员
当类中成员是其他类的对象时,称该成员是对象成员。
构造函数的调用顺序是,先调用对象成员的构造,在调用本类构造;构造函数的调用顺序与构造函数的调用顺序相反。
2.8 静态成员变量-静态成员函数
静态成员变量:
1)所有对象共享同一份数据;
2)在编译阶段分配内存;
3)类内声明,类外初始化。
静态成员函数:
1)所有对象共享同一个函数;
2)静态成员函数只能访问静态变量。
注意:非静态成员变量引用必须与特定对象对应,由于静态成员函数不知道该变量是哪个对象的属性,因而不可以访问非静态成员变量。
#include <iostream>
using namespace std;
class Person
{
public:
static void func()
{
m_A = 100; //静态成员函数可以访问静态成员变量
// m_B = 200; 静态成员函数不可以访问非静态成员变量,无法区分到底是哪个对象的属性
cout << "static void func调用" << endl;
}
static int m_A; // 静态成员变量
int m_B; // 非静态成员变量
private:
static void func2()
{
cout << "static void func2调用" << endl;
}
};
int Person::m_A = 0; // 类外初始化
void test()
{
//1.通过对象访问
Person p;
p.func();
//2.通过类名进行访问
Person::func();
//Person::func2(); 类外访问不到私有静态成员函数
}
int main()
{
test();
system("pause");
return 0;
}
3 C++对象模型
3.1 成员变量和成员函数分开存储
在C++中,类内的成员变量和成员函数分开存储,只有非静态成员变量才属于类的对象上。
空对象占用内存空间为1个字节;C++编译器给每个空对象分配一个字节空间,是为了区分空对象占内存的位置;每个空对象也应该有一个独一无二的内存地址。
class Person
{
int m_A; // 非静态成员变量 属于类对象上
static int m_B; // 静态成员变量 不属于类对象上
void func() {} // 非静态成员函数 不属于类对象上
static void func2() {} // 静态成员函数 不属于类对象上
};
int Person::m_B = 0; // 类外初始化
void test()
{
Person p;
cout << "size of p= " << sizeof(p) << endl;
}
int main()
{
test();
system("pause");
return 0;
}
上述代码中,Person类中加上静态成员变量、静态成员函数和非静态成员函数后,p的大小也一直为4个字节,即非静态成员变量m_A所占的内存大小。
总结:非静态成员变量属于类对象上;静态成员变量、静态成员函数和非静态成员函数均不属于类的对象上。
3.2 this指针概念
this指针指向被调用的成员函数所属的对象。
用途:
1)当形参和成员变量同名时,可用this指针来区分;
2)在类的非静态成员函数中返回对象本身,可用return *this。
#include <iostream>
using namespace std;
class Person
{
public:
Person(int age)
{
// 当形参和成员变量同名时,可用this指针来区分
this->age = age; // this指针指向被调用的成员函数所属的对象
}
Person & Personaddperson(Person p)
{
this->age += p.age;
// this指向p2的指针,而*this指向的就是p2这个对象本体
return *this; //返回对象本身
}
int age;
};
void test()
{
Person p1(10);
cout << "p1.age= " << p1.age << endl;
Person p2(18);
// 链式编程思想
p2.Personaddperson(p1).Personaddperson(p1);
cout << "p2.age= " << p2.age << endl;
}
int main()
{
test();
system("pause");
return 0;
}
注意:Personaddperson函数一定要用引用的方式返回,因为用值的方式返回,调用拷贝构造函数时会复制一份新的对象,而引用的方式返回的一直是p2这个本体对象。
3.3 空指针访问成员函数
C++中空指针可以调用成员函数,但是成员函数中用到了this指针,就需要加以判断,保证代码的健壮性。
3.4 const修饰成员函数
常函数:成员函数后加const,称之为常函数;常函数内部不可以修改成员属性;成员属性声明时加关键字mutable后,在常函数中就可以修改。
常对象:声明对象前加const,称之为常对象;常对象只能调用常函数;常对象不可以调用普通成员函数,因为普通成员函数可以修改属性。
#include <iostream>
using namespace std;
class Person
{
public:
// this指针的本质是指针常量,
// 等价于Person *const this;
// 在成员函数后面加上const,相当于 const Person *const this,修饰的是this指针,让指针指向的值也不可以修改
void showPerson() const // 常函数
{
// this->m_A = 100 ; 错误
this->m_B = 200; // 正确
}
void func()
{
m_A = 100; // 可修改成员属性
}
int m_A;
mutable int m_B; // 加关键字mutable后,在常函数中就可以修改
};
void test01()
{
Person p;
p.showPerson;
}
void test02()
{
const Person p; // 常对象
p.showPerson(); // 常对象只能调用常函数
p.func(); // 错误,常对象不可以调用普通成员函数,因为普通成员函数可以修改属性
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
4 友元
友元的目的是让一个函数或者类访问另一个类中的私有成员。
关键字:friend
友元的三种实现:全局函数做友元、友元类、成员函数做友元。
4.1 全局函数做友元
#include <iostream>
using namespace std;
#include<string>
class Building
{
friend void GoodGay(Building *building);
public:
Building()
{
m_Sittingroom = "客厅";
m_Bedroom = "卧室";
}
public:
string m_Sittingroom;
private:
string m_Bedroom;
};
// 全局函数
void GoodGay(Building *building)
{
cout << "好朋友的全局函数 正在访问:" << building->m_Sittingroom << endl;
cout << "好朋友的全局函数 正在访问:" << building->m_Bedroom << endl;
}
void test()
{
Building building;
GoodGay(&building);
}
int main()
{
test();
system("pause");
return 0;
}
4.2 类做友元
#include <iostream>
using namespace std;
#include<string>
// 类作友元
class Building;
class GoodGay
{
public:
GoodGay();
void visit(); // 访问Building中的属性
Building * building;
};
class Building
{
// GoodGay是本类的好朋友,可以访问本类中的私有成员
friend class GoodGay;
public:
Building();
public:
string m_Sittingroom;
private:
string m_Bedroom;
};
// 类外写成员函数
Building::Building()
{
m_Sittingroom = "客厅";
m_Bedroom = "卧室";
}
GoodGay::GoodGay()
{
// 创建建筑物对象
building = new Building;
}
void GoodGay::visit()
{
cout << "好朋友类正在访问:" << building->m_Sittingroom << endl;
cout << "好朋友类正在访问:" << building->m_Bedroom << endl;
}
void test()
{
GoodGay gg;
gg.visit();
}
int main()
{
test();
system("pause");
return 0;
}
4.3 成员函数做友元
#include <iostream>
using namespace std;
#include<string>
// 成员函数做友元
class Building;
class GoodGay
{
public:
GoodGay();
void visit(); // 只让该函数访问GoodGay的私有属性
void visit2();
private:
Building *building;
};
class Building
{
friend void GoodGay::visit();
public:
Building();
public:
string m_Sittingroom;
private:
string m_Bedroom;
};
Building::Building()
{
this->m_Sittingroom = "客厅";
this->m_Bedroom = "卧室";
}
// 类外写成员函数
GoodGay::GoodGay()
{
// 创建建筑物对象
building = new Building;
}
void GoodGay::visit()
{
cout << "visit函数正在访问:" << building->m_Sittingroom << endl;
cout << "visit函数正在访问:" << building->m_Bedroom << endl;
}
void GoodGay::visit2()
{
cout << "visit2函数正在访问:" << building->m_Sittingroom << endl;
//cout << "visit2函数正在访问:" << building->m_Bedroom << endl;
}
void test()
{
GoodGay gg;
gg.visit();
gg.visit2();
}
int main()
{
test();
system("pause");
return 0;
}
未完待续,敬请期待下一篇博客:C++入门基础教程(六):类和对象(下)
结束语
大家的点赞和关注是博主最大的动力,博主所有博文中的代码文件都可分享给您(除了少量付费资源),如果您想要获取博文中的完整代码文件,可通过C币或积分下载,没有C币或积分的朋友可在关注、点赞和评论博文后,私信发送您的邮箱,我会在第一时间发送给您。博主后面会有更多的分享,敬请关注哦!
以上是关于C++入门基础教程:类和对象(中)的主要内容,如果未能解决你的问题,请参考以下文章
黑马程序员 C++教程从0到1入门编程笔记5C++核心编程(类和对象——继承多态)