类与对象(一)
访问权限
类在设计时共有三种权限:
相比JAVA的权限控制少了一个,更简洁明了了。
public 公共权限,类内类外都可访问。
protected 保护权限,类内可以访问,类外不可访问,但子类可访问父类中protected权限的成员。
private 私有权限,仅限类内访问。
class和struct的区别
名称不同,一个为自定义数据结构,一个为类,反正我以前都是把struct当成小型的自定义结构用,也没有往里面塞方法之类的。
唯一区别在于默认访问权限不同,class默认为私有,struct默认为公共的。
对象的初始化和清理
构造函数和析构函数,要求强制实现,要么自己定义,要么采用编译器自带的空实现。
构造函数的分类和调用
最常用的还是括号法。
构造函数的调用规则
浅拷贝和深拷贝
拷贝构造函数的分类,默认的拷贝构造函数为浅拷贝,可以理解为就是简单的值传递,这样会导致类中成员为指针变量,进行拷贝操作时会导致不同对象的指针指向同一个堆区空间,这样在析构函数释放时就会导致对于堆区空间的强制释放。代码如下:
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
Person(int age, int height)
{
m_age = age;
m_height = new int(height);
}
~Person()
{
if (m_height != NULL)
{
delete m_height;
m_height = NULL;
}
cout << "调用了Person的析构函数" << endl;
}
int m_age;
int* m_height;
};
int main(){
Person p1 = Person(18,173);
cout << p1.m_age << *p1.m_height << endl;
Person p2 = Person(p1);
cout << p2.m_age << *p2.m_height << endl;
return 0;
}
解决办法就是自己写拷贝构造函数,在每次新生成对象时,再独自从堆区开辟一个空间分配即可。代码如下:
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
Person(int age, int height)
{
m_age = age;
m_height = new int(height);
}
Person(const Person& p)
{
if (p.m_height != NULL)
{
m_height = new int(*p.m_height);
}
m_age = p.m_age;
}
~Person()
{
if (m_height != NULL)
{
delete m_height;
m_height = NULL;
}
cout << "调用了Person的析构函数" << endl;
}
int m_age;
int* m_height;
};
int main(){
Person p1 = Person(18,173);
cout << p1.m_age << *p1.m_height << endl;
Person p2 = Person(p1);
cout << p2.m_age << *p2.m_height << endl;
return 0;
}
初始化列表
在构造函数中,可以以下方式进行初始化成员变量。
类名(形参):成员变量(初值),成员变量(初值),...
{
}
例子如下:
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;
}
类对象作为类成员时析构和构造的顺序
当一个类对象(a)作为另一个类(b)的成员时,先调用a的构造,后构造b的构造。析构时先调用b的析构,后调用a的析构。
栈的模式先进后出,b的构造时,需要利用a的信息,所以a应该先于b的构造,析构时,只有当a知道b要析构时,才会调用自己的析构,所以b的析构才会先于a的析构。
静态成员
静态成员记住一个特性,所有同一个类的对象共享,所以静态成员是依托于类而存在的,而不是依托于实例化的对象存在的。
所以静态成员变量实在编译阶段就分配内存了,在全局区。静态成员重要特性:类内声明,类外初始化!!!通过类名去访问静态成员变量或者静态成员函数时加上作用域::即可。
例子见下述博客:
https://blog.csdn.net/weixin_54907221/article/details/118494341
成员变量与成员函数存储
在类的存储上,只有非静态成员变量是和对象绑定的,静态成员变量存储在全局区中,函数不管静态和非静态也都是只存储一份,所有对象共享的调用,其中非静态成员函数根据this指针辨别是哪一个对象的调用。
this指针
this指针,之前学习JAVA的时候有所了解,但在类的非静态函数中返回对象本身,还是挺稀奇的。
这里有两个例子,关于返回对象本身。
链式编程
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
Person(int a)
{
this->a = a;
}
Person& add(int a)
{
this->a += a;
return *this;
}
int a;
};
int main(){
Person p(10);
p.add(10).add(10).add(10);
cout << p.a << endl;
return 0;
}
通过引用的方式,返回对象本身,这样就可以一直不断的接.add()了,同理观察cout<<的结构也是一样的,说明cout的设计也是同理。
这里就有人问了,为什么要用引用的形式返回,直接返回Person会怎么样,可见下述代码:
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
Person(int a)
{
this->a = a;
}
Person add(int a)
{
this->a += a;
return *this;
}
int a;
};
int main(){
Person p(10);
p.add(10).add(10).add(10);
cout << p.a << endl;
return 0;
}
代码运行结果为20,只被加了一次,原因是Person类型的返回是返回一个匿名对象,并没有被任何对象接受,这就导致了p.a只被接受一次。
const修饰成员函数和对象
当const修饰成员函数时,放在函数之后,不可以修改成员属性,成员属性中有关键字mutable时,才可以修改。
在实例化对象时,若加const修饰,则为常对象,常对象只能调用常函数和修改mutable的成员变量。
友元
友元是指允许函数或者类访问另一个类中的私有成员的关键字,friend。
全局函数做友元
全局函数做友元,需要将全局函数的声明放入类中,且在其上方加入friend。
#include<iostream>
#include<string>
using namespace std;
class Building
{
//goodGay全局函数是Building的好朋友,可以访问Building的私有成员
friend void goodGay(Building& building);
public:
Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
string m_SittingRoom;
private:
string m_BedRoom;
};
//全局函数
void goodGay(Building &building)
{
cout << "好基友的全局函数 正在访问:" << building.m_SittingRoom << endl;
cout << "好基友的全局函数 正在访问:" << building.m_BedRoom << endl;
}
void test01()
{
Building building;
goodGay(building);
}
int main(){
test01();
return 0;
}
类做友元
将友元类放入当前类中,前加friend。
#include<iostream>
#include<string>
using namespace std;
class Building
{
//GoodGay类作为Building类的私有成员,可以访问其私有成员
friend class GoodGay;
public:
Building();
string m_SittingRoom;
private:
string m_BedRoom;
};
Building::Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
class GoodGay
{
public:
GoodGay();
void visit(); //参观函数,访问Building中的属性
Building* building;
};
GoodGay::GoodGay()
{
//创建一个建筑物的对象
building = new Building;
}
void GoodGay::visit()
{
cout << "好基友类正在访问 " << building->m_SittingRoom << endl;
cout << "好基友类正在访问 " << building->m_BedRoom << endl;
}
void test01()
{
GoodGay gg;
gg.visit();
}
int main(){
test01();
return 0;
}
成员函数做友元
成员函数做友元,语法题先为frieng void 类名::成员函数名();
注意之间声明的顺序,A要做B的友元,A可以访问B的私有成员,则在B中写上述语句,且A得在B之前声明。
#include<iostream>
#include<string>
using namespace std;
class Building;
class GoodGay
{
public:
GoodGay();
void visit(); //参观函数,访问Building中的私有属性
void visit2();//不可访问私有属性
Building* building;
};
class Building
{
friend void GoodGay::visit();
public:
Building();
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 GoodGay::visit2()
{
cout << "好基友类正在访问 " << building->m_SittingRoom << endl;
}
void test01()
{
GoodGay gg;
gg.visit();
gg.visit2();
}
int main(){
test01();
return 0;
}
运算符重载
运算符重载和自定义运算符的本质都是定义了一个operator+运算符的函数。
加法重载:
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
Person()
{
}
Person operator+(const Person& p)
{
Person temp;
temp.m_a = this->m_a + p.m_a;
temp.m_b = this->m_b + p.m_b;
return temp;
}
Person(int a,int b)
{
m_a = a;
m_b = b;
}
int m_a;
int m_b;
};
void test01()
{
Person p1(10,20);
Person p2(20, 10);
Person p3 = p1 + p2;
cout << p3.m_a << ' ' << p3.m_b << endl;
}
int main(){
test01();
return 0;
}
左移运算符
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
//一般不会利用成员函数去重载左移运算符,因为这样会导致p在左边
/*void operator<<(cout )
{
}*/
Person()
{
}
Person(int a,int b)
{
m_a = a;
m_b = b;
}
int m_a;
int m_b;
};
//只能利用全局函数重载左移
ostream& operator<<(ostream &cout,Person &p)
{
cout << "m_a = " << p.m_a << ' ' << "m_b = " << p.m_b << endl;
return cout;
}
void test01()
{
Person p(10,20);
cout << p << endl;
}
int main(){
test01();
system("pause");
return 0;
}
递增运算符重载
#include<iostream>
#include<string>
using namespace std;
//重载递增运算符
class MyInteger
{
friend ostream& operator<< (ostream& cout, MyInteger myint);
public:
MyInteger()
{
m_Num = 0;
}
//重载前置++运算符,用返回引用方便链式编程
MyInteger& operator++()
{
m_Num++;
return *this;
}
//重载后置++运算符,用int占位参数,用以区分是前置和后置递增。
//注意返回值,返回引用的话就会导致返回局部变量的地址了,非法操作。
MyInteger operator++(int)
{
MyInteger temp = *this;
this->m_Num++;
return temp;
}
private:
int m_Num;
};
ostream& operator<< (ostream& cout,MyInteger myint)
{
cout << myint.m_Num << endl;
return cout;
}
void test01()
{
MyInteger myint;
cout << ++myint << endl;
cout << myint++ << endl;
cout << myint << endl;
}
int main(){
test01();
system("pause");
return 0;
}
赋值运算符重载
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
Person(int age)
{
m_Age = new int(age);
}
~Person()
{
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
}
//重载赋值运算
Person& operator=(Person &p)
{
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
m_Age = new int(*p.m_Age);
return *this;
}
int* m_Age;
};
void test01()
{
Person p1(18);
Person p2(20);
Person p3(30);
p3 = p2 = p1;
cout << "p1的年龄为:" << *p1.m_Age << endl;
cout << "p2的年龄为:" << *p2.m_Age << endl;
cout << "p3的年龄为:" << *p3.m_Age << endl;
}
int main(){
test01();
system("pause");
return 0;
}
重载函数调用,仿函数
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
//重载小括号
void operator() (string st)
{
cout << st << endl;
}
};
void prints(string st)
{
cout << st << endl;
}
void test01()
{
Person ps;
ps("Hello World");//因为和函数很类似,所以称为仿函数。
prints("Hello World");
Person()("Hello World"); // 使用匿名对象使用仿函数。
}
int main(){
test01();
system("pause");
return 0;
}