C++ 基础04
对象数组
存储对象的数组
分类
静态对象数组 类名 数组名[长度] = {对象1,对象2,...};
动态对象数组 类名* 数组名 = new[长度]{对象1,对象2,对象3,...};
>注意:动态数组需要释放
>语法为:delete [] 数组名
静态成员
概述
使用static修饰的成员
分类
静态成员变量
语法:static 数据类型 变量名;
注意:必须在类中定义,类外初始化
静态的成员变量属于该类的所有对象,该类的所有对象共同拥有
静态成员变量可以直接使用类名调用,
语法:类名::静态成员变量名
静态成员函数
语法:static 返回值类型 函数名(形参列表)
{
函数体
}
注意:不能使用this关键字,则该函数中不能使用本类的非静态成员
可以使用类名直接调用,语法为
类名::静态成员函数(实参列表)
也可以使用对象名,函数名(实参列表)调用
单例模式
引入
代码设计的模块之一.是一种解决问题的思路,而单例模式解决的问题是:一个类只能创建一个对象
分类
懒汉式
1>私有化构造函数(使类外无法创建该类的对象
2>提供公共的的静态成员函数返回该类对象(保证返回的该类对象是唯一的)
3>在该类中定义一个静态成员该成员的数据类型就是该类,该成员变量默认为NULL
4>在步骤2提供的函数中判断步骤3的成员变量是否为NULL,如果为空则创建该类对象,如果不为空直接返回
注意:存在线程不安全问题
饿汉式
1>私有化构造函数(类外无法创建该类对象)
2>提供公共化静态成员函数返回该类的对象,(保证返回该类对象的唯一性)
3>在该类中定义一个静态成员,该成员的数据类型就是该类,该成员的变量默认为创建的该类对象
4>在步骤2提供的函数中直接返回步骤3创建的对象
注意:线程安全,但是有些浪费内存
对象的存储
静态成员在类加载之初被加载,不占对象的空间
非静态成员加载在对象中
成员函数加载到代码区
友元
作用
A的友元函数或者友元类可以直接访问A的私有成员
关键字
friend
注意
友元不能继承 友元不能被继承 友元关系是单向的 友元打破类的封装性
friend修饰
可用来修饰全局函数,成员函数,一整个类,三种,所以友元也分为三种
分类
友元全局函数
步骤:
1>定义一个类
2>定义一个函数
3>在1>类中声明2>中的函数为友元函数
语法:friend 返回值类型 函数名(形参列表);
4>在2>定义的函数可以访问1>类中的私有成员(即被private修饰的成员)
友元成员函数
步骤:
声明一个类,如A 语法:class A;
定义一个B类
在B类中声明一个函数,该函数为A的友元函数,该函数只声明,不实现
定义A类
声明B类中的函数为A类的友元函数,在A类中书写
语法:friend 返回值类型 B::函数名(行参列表);
实现B类声明的函数
如:
string
概述
是一种随字符串进行操作的类,所需头文件为string
使用
语法:string 变量名 = 值; 如:string str = "abc"; 原因:隐式转换
输出
可以通过cout之间输出,显示其中的内容 原因:重载<<运算符
赋值
语法:string 变量名 = string 变量名;
字符串拼接
语法:变量名1 = 变量名2 + 变量名3; 变量名1 += 变量名2;
输入
语法:cin >> 变量名;
比较内容是否相同
语法:变量名1 == 变量名2;
c_str
语法:const char *变量名 = string对象名.c_str();
作用:将c++的string类的对象转换为C语言的字符数组,需注意浅拷贝
如:
string str07 = "hello";
char *str08 = (char *)(str07.c_str());
cout << "str08 = " << str08 << endl;
str08[0] = 'T';
cout << "str08[0] = " << str08[0] << endl;
cout << "str07 = " << str07 << endl; cout << "str08 = " << str08 << endl;
length
语法:int 变量名 = string对象名.length();
作用:测量string对象中存储的字符串长读,不包含\0;
重载运算符
概述
是对已有的运算符进行重新定义,赋予其另一种功能,以适用不同的数据类型
关键字
operator
语法
返回值类型 operator运算符(即要重新定义的运算符)(形参列表)
{
函数体
}
如:>>
void operotra >>(形参列表)
{
}
思路
1、分析运算符的运算对象的个数
2、分析运算符左边的运算对象是 自定对象 还是其他
左边:是其他 只能全局函数实现 (必须使用友元),有几个元素参与就有几个参数,第一个
参数是符号左边的,第二个参数是符号右边
左边:自定义对象
可以用使用全局函数重载运算符(参数个数 和 运算符对象的个数一致)
也可以使用成员函数重载运算符(参数可以少一个) (推荐)
注意
尽量不要重载&& ||,无法实现&& || 短路
示例1:重载<<,>>运算符
题目:自定义学员类,使其可以通过cout输出其对象的成员变量的值
分析:
cout << s << endl;
第一个<<需要通过全局函数重载<<运算符,使其可以打印学生类的信息,并返回cout以便于完成第二个<<的输出
步骤:
1,定义学生类
2,使用全局函数重载<<运算符,在该函数中打印学生类对象的信息,该函数两个参数,第
一个参数是cout,第二个参数是要打印的学生类对象,返回值为cout
3,将步骤2的函数声明为步骤1中定义类的全局友元函数
4,定义main函数
5,在main函数中创建学生类对象
6,使用重载<<函数
代码:
#include <iostream>
#include <string>
using namespace std;
class Stu{
friend ostream& operator<<(ostream& o,Stu &s);
private:
string name;
int age;
public:
Stu(string name,int age):name(name),age(age){}
void show_info()
{
cout << name << "\t" << age << endl;
}
};
ostream& operator<<(ostream& o,Stu &s)
{
o << s.name << "\t" << s.age;
}
int main(int argc, char const *argv[])
{
Stu s1("张三",18);
Stu s2("德玛",21);
cout << s1 << endl;
return 0;
}
示例2:重载+运算符
题目:自定义学员类,创建其对其,使其可以通过+使其年龄增长
分析:s + 2
方案1:使用全局函数重载+
#include <iostream>
#include <string>
using namespace std;
class Stu{
friend void operator>>(istream& i,Stu& s);
friend void operator+(Stu& s,int age);
private:
string name;
int age;
public:
Stu(string name,int age):name(name),age(age){}
void show_info()
{
cout << name << "\t" << age << endl;
}
};
void operator>>(istream& i,Stu& s)
{
i >> s.name >> s.age;
}
void operator+(Stu& s,int age)
{
s.age = s.age + age;
}
int main()
{
Stu s("abc",18);
cin >> s;
s.show_info();
s + 3;
s.show_info();
return 0;
}
方案2:使用成员函数重载+
#include <iostream>
#include <string>
using namespace std;
class Stu{
friend void operator>>(istream& i,Stu& s);
private:
string name;
int age;
public:
Stu(string name,int age):name(name),age(age){}
void show_info()
{
cout << name << "\t" << age << endl;
}
void operator+(int age)
{
this->age += age;
}
};
void operator>>(istream& i,Stu& s)
{
i >> s.name >> s.age;
}
int main()
{
Stu s("abc",18);
cin >> s;
s.show_info();
s + 3;
s.show_info();
return 0;
}
示例3:重载==运算符
题目
判断两个对象的属性值是否相同
分析
Stu s1("张三",18);
Stu s2("张三",18);
bool b = s1 == s2;
示例4:重载++运算符
注意
++运算符分为++在前与++在后两种所以需要重载两种
当编译器看到++a(前置++),它就调用 operator++(Type& a)(全局函数),operator++ ()(成员函数)
当编译器看到a++(后置++),它就会去调用 operator++(Type& a,int)(全局函 数),operator++(int)(成员函数)
题目
++使学院年龄+1
示例5:重载*与->
智能指针
代码分析问题
#include <iostream>
using namespace std;
class Data{
private:
int x,y,z;
public:
Data(){
cout << "无参构造函数" << endl;
}
Data(int a,int b,int c):x(a),y(b),z(c){
cout << "有参构造函数" << endl;
}
~Data()
{
cout << "析构函数" << endl;
}
};
int main(int argc, char *argv[])
{
Data *p = new Data();
return 0;
}
观察以上代码,我们发现创建的对象没有被销毁,但是我们在编写代码时经常会忘记销毁,那该怎么办呢?
解决方案如下
#include <iostream>
using namespace std;
class Data{
private:
int x,y,z;
public:
Data(){
cout << "无参构造函数" << endl;
}
Data(int a,int b,int c):x(a),y(b),z(c){
cout << "有参构造函数" << endl;
}
~Data()
{
cout << "析构函数" << endl;
}
};
class FreeData{
private:
Data* p;
public:
FreeData(){
p = NULL;
}
FreeData(Data* data){
p = data;
}
~FreeData(){
if(p != NULL)
{
delete p;
p = NULL;
}
}
};
int main(int argc, char *argv[])
{
FreeData fd(new Data(1,2,3));
return 0;
}
现在我们发现Data对象可以销毁,但是如何调用其对象中的属性呢?
方案如下
#include <iostream>
using namespace std;
class Data{
private:
int x,y,z;
public:
Data()
cout << "无参构造函数" << endl;
}
Data(int a,int b,int c):x(a),y(b),z(c){
cout << "有参构造函数" << endl;
}
~Data()
{
cout << "析构函数" << endl;
}
int getX()
{
return x;
}
};
class FreeData{
private:
Data* p;
public:
FreeData(){
p = NULL;
}
FreeData(Data* data){
p = data;
}
~FreeData(){
if(p != NULL)
{
delete p;
p = NULL;
}
}
Data& operator *()
{
return *p;
}
Data* operator ->()
{
return p;
}
};
int main(int argc, char *argv[])
{
FreeData fd(new Data(1,2,3));
cout << (*fd).getX() << endl;
cout << fd->getX() << endl;
return 0;
}
示例6:重载()
#include <iostream>
using namespace std;
class Data{
public:
int x,y,z;
public:
Data()
{
}
Data(int x,int y,int z):x(x),y(y),z(z){}
void operator()(int x,int y,int z)
{
this->x = x;
this->y = y;
this->z = z;
}
};
ostream& operator<<(ostream& o,Data& d)
{
o << d.x << "\t" << d.y << "\t" << d.z;
return o;
}
int main(int argc, char const *argv[])
{
Data d;
d(1,2,3);
d(4,5,6);
cout << d << endl;
return 0;
}
示例7:重载=
注意
=重载时,可能会调用类本身的拷贝构造函数。
如果左值是没有创建的对象时,会调用拷贝构造函数.
如果左值是已创建的类对象,会执行 =重载函数,实现数据的拷贝。
如:
#include <iostream>
#include <string>
using namespace std;
class Data{
public:
int x,y,z;
public:
Data()
{
}
Data(int x,int y,int z):x(x),y(y),z(z){}
Data(const Data& d)
{
cout << "拷贝构造" << endl;
}
void operator=(Data& d)
{
cout << "operator=" << endl;
this->x = d.x;
this->y = d.y;
this->z = d.z;
}
};
int main(int argc, char const *argv[])
{
// string str01 = "hello";
// string str02 = str01; //拷贝构造
// string str03 = "world";
// str03 = str01;
Data d1 = Data(1,2,3);
Data d2;
d2 = d1;
cout << d2.x << d2.y << d2.z << endl;
return 0;
}