C++封装

一、封装
1、封装作用:对外提供接口,屏蔽数据,对内开放数据。
2、C语言的封装:当单一变量无法完成描述需求的时候,封装成函数或结构体类型解决。
问题:即知其接口,又可以直接访问其内部数据。
注:C语言中的封装内容不能是函数(C++中的class可实现),结果可以是函数。
3、C++的封装:class 封装的本质,在于将数据和行为绑定在一起,再通过对象来完成操作。
二、类与对象
1、类的声明:
class 类名称
{
public:
公有成员(外部接口)
private:
私有成员
protected:
保护成员
};
2、权限修饰符:public、private(set/get)、protected
①public后面声明,它们是类与外部的接口,任何外部函数都可以访问公有类型数据和函数。
②private后面声明,只允许本类中的函数访问,而类外部的任何函数都不能访问。
③protected后面声明,与private类似,其差别表现在继承与派生时对派生类的影响不同。
3、class的get/set(在公有中获取私有变量)方法:提供相对安全方式访问成员变量。
4、面向对象编程实例:栈的实现。
5、面向对象的代码结构:使用.h定义类,使用.cpp实现类里的方法,主函数main.cpp。
5、C++栈的实现示例:

Stack.h文件 Stack.cpp文件(1)

Stack.cpp文件(2) main.cpp文件
三、class的构造函数

1、作用:初始化对象的属性。
2、特点:①没有函数返回值;②函数名与类型相同;
③可以重载;④实例(定义)一个对象会自动调用构造函数。
3、种类:
①默认的无参构造函数;②无参构造函数;③类型转换构造函数;
④默认拷贝构造函数;⑤移动拷贝构造函数(、⑥初始化列表)。
4、使用示例:
(1)默认的无参构造函数:类里没有任何自定义的构造函数时,会默认生成Test()。
class Test
{
public:
…….
//此处无特意自定义的无参构造函数
//则默认生成一个Test()
private:
int m_num;
int m_age;
string m_name;
char *m_addr;
};
Int main()
{
Test t;
//会调用默认生成的无参构造函数
………
return 0;
}
(2)自定义的无参构造函数
class Test
{
public:
Test() //无参的构造函数
{
m_num = 0;
m_age = 0;
m_name = “jsetc”;
m_addr = new char[20];
cout << “Test” << endl;
}
Test(int num,int age,string name,const char *addr) //重载的构造函数
{
m_num = num;
m_age = age;
m_name = name;
m_addr = new char[20];
strcpy(m_addr,addr);
cout << “Test(int num,int age, string name,char *addr)” << endl;
}
…….
private:
…….
};
int main()
{
Test t(1,23,“zhangsan”,“nanjing”);
//以上方重载的函数为例,会自动调用构造函数
cout << t.getName() << endl; // getName()在public:中
return 0;
}
(3)类型转换构造函数:一个参数的构造函数;
注:存在风险–将其他类型默认转换为类类型;通过explicit修饰构造函数,防止发生默认转换。
class Test
{
public:
………
explicit Test (int num)
//类型转换构造函数:explicit防止发生隐式类型转换
{
cout << " Test int" << endl;
m_num = num;
}
…….
private:
…….
};
int main()
{
Test a1{1}; //std::initializer_list
//类中自动生成 Test (std::initializer_list l)
//但当类中有系统自己定义的构造函数时,系统不会默认生成
Test a2 = {2};
// Test a3 = 3; //错误:将一个整型常量赋值给一个Test类型的对象
//发生了隐式类型转换(存在风险)解决方法:加explicit
……
return 0;
}
(补充): 使用static_cast转换类型,需在public中添加operator。
class Test
{
public:
…….
operator int() //类类型转换运算符重载:此处支持static_cast()
{
return m_num;
}
operator string() //支持static_cast()
{
return m_name;
}
};
int main()
{
Test a4;
int num = static_cast(a4);
cout << num << endl;
string name = static_cast(a4);
………
return 0;
}
(4) 默认的拷贝构造函数:当类中无拷贝构造函数时,系统会默认生成一个拷贝构造函数: Test (const Test &a);
(5) 自定义拷贝构造函数(拷贝!= 赋值)
class Test
{
public:
…….
Test (const Test &other) //自定义的拷贝函数
{
m_num = other.m_num;
cout << " Test cope Test " << endl;
}
private:
…….
};
int main()
{
Test a1(a);//调用拷贝构造函数Test (const Test &other)
Test a3 = a;//调用拷贝构造函数Test (const Test &other),若未定义报错
Test a4 = {1}; //initializer_list C++可变参数
Test a5 = {a1};
// Test a2; a2 = a;//赋值运算符 != 拷贝
return 0;
}
(补充):赋值(等号=)运算符重载(即上方的Test a2; a2 = a;)
Test& operator =(const Test &other) //添加在类中验证赋值运算符 != 拷贝
{
cout << " Test operator Test " << endl;
m_num = other.m_num;
}
四、class的析构函数
1、作用:释放对象给属性分配的空间。
2、特点:①没有返回值;②不能重载;
③函数名:~类名;④当实例的对象释放空间时被调用。
3、使用示例:
class Test
{
public:
………
~Test() //析构函数,main中使用Test+变量名 会自动调用
{
cout << “~Test” << endl;
delete m_addr; //若无此语句,m_addr所占资源将无法释放
//可释放构造函数中的变量char *m_addr的空间
//或者下方方式
//if( m_addr != NULL)
//{
// free(m_addr);
// m_addr = NULL;
//}
}
………
private:
………
};
五、深拷贝与浅拷贝
1、浅拷贝:对基本类型数据以及简单的对象直接复制内存的拷贝。但是,当类的成员包含指针的时候,使用浅拷贝是将拷贝对象的地址赋值给了新的对象,导致两个指针指向了同一块内存空间,这时候浅拷贝就不能满足实际要求了,需使用深拷贝。

示例: Test (const Test &other)
{
m_id = other.m_id;
m.name = other.m_name;
}
2、深拷贝:当类持有其它资源(如动态分配的内存、指向其他数据的指针等)时使用的拷贝。此时默认的拷贝构造函数不能拷贝这些资源,我们必须显式地定义拷贝构造函数,以完整地拷贝对象的所有数据。
注:​对于简单的类,默认的拷贝构造函数一般就够用了,我们也没有必要再显式地定义一个功能类似的拷贝构造函数。

示例: Test (const Test &other)
{
m_id = other.m_id;
m_name = new char[100];
strcpy_s(m_name, strlen(other.m_name). other.m_name);
}
Test() = default; //提高代码的可读性
//当没有任何析构函数时,系统回默认生成无参的构造函数
六、移动构造函数
1、移动拷贝构造函数:Test (const Test && other){}
2、移动赋值运算符:Test & operator=( Test && other){}
注:①形参为对象,若不用引用作形参而使用拷贝,操作耗时、开销大。
②&左值引用,&&右值引用,使用std::move()可将左值转为右值。
③若没有声明,系统会自动生成默认的移动构造函数。
3、引入右值引用的目的:提高程序运行效率、把拷贝对象变成移动对象,省去调用拷贝构造函数,拷贝赋值构造函数,省去开辟空间过程;

你可能感兴趣的:(C++封装)