面向对象三大特性:封装、继承、多态
1.封装
1.1封装的意义
1.将属性和行为作为一个整体,表现生活中的事物 语法:calss 类名{ 访问权限:属性 / 行为};
示例
#include<iostream>
#include<string>
using namespace std;
const double PI = 3.14; //定义圆周率
class Circle //class代表设计一个类,类后面紧跟着的就是类名称
{
public://访问权限中的公共权限
int m_r;//属性:定义圆的半径
double perimeter() //行为:获取圆的周长
{
return 2 * PI * m_r;
}
};
int main() {
//实例化,通过一个类创建一个对象的过程
//(通过圆类,创建具体圆的对象:周长)
Circle c1;
c1.m_r = 10;//给圆对象的属性进行赋值
cout << "c1周长为: " << c1.perimeter() << endl;
return 0;
}
2.将属性和行为加以权限控制 三种访问权限: public 公共权限 类内可以访问,类外可以访问 protected 保护权限 类内可以访问,类外不可以访问 pirvate 私有权限 类内可以访问,类外不可以访问
在类内函数func中,函数可以访问公共权限的m_Name,保护权限的m_car,私有权限的m_password
而在类外函数main中,函数只能访问公共权限的m_Name
1.2 struct和class的区别
class作为类其默认的访问权限为私有,struct作为类时其默认访问权限为公有
1.3 成员属性设置为私有
1.将所以成员属性设置为私有,可以自己控制读写权限
#include<iostream>
#include<string>
using namespace std;
class Li
{
public:
void setName(string Name)//设置名字可写
{
m_Name = Name;
}
string getName() //设置名字可读
{
return m_Name;
}
string getcar() //设置汽车可读
{
return m_car;
}
void setpassword(int password)
{
m_password= password ;//设置密码可写
}
private:
string m_Name;//可读可写
string m_car="奔驰"; //可读不可写
string m_password; //可写不可读
};
int main() {
Li c1;
c1.setName("wang");//名字设置
cout << "名字为:" << c1.getName() << endl; //获取名字
cout << "汽车为: " << c1.getcar() << endl; //获取汽车
c1.setpassword(123456); //密码设置
return 0;
}
2.对于写权限我们可以自己检测数据的有效性
#include<iostream>
#include<string>
using namespace std;
const double PI = 3.14;
class Li
{
public:
void setage(int age) {
if (age<0 || age > 150)//对于写权限我们可以自己检测数据的有效性
{
cout << "您输入的年龄" << age << "有误,请更正!" << endl;
return;
}
m_age=age;
}
int getage() {
return m_age;
}
private:
int m_age=0;
};
int main() {
Li c1;
c1.setage(60);
cout << "年龄为:" << c1.getage() <<"岁" << endl;
return 0;
}
若输出值超过150或者小于0则会显示
2.对象的初始化和清理
2.1构造函数和析构函数
构造函数进行初始化操作
语法:类名(){}
1.构造函数没有返回值也不写void
2.函数名称与类名相同
3.函数可以有参数,因此可以发生重载
4.程序在调用对象时会自动调用构造,无需手动调用,而且只会调用一次
析构函数 进行清理操作
语法:~类名(){}
1.构造函数没有返回值也不写void
2.函数名称与类名相同在名称前加~
3.函数不可以有参数,因此不可以发生重载
4.程序在对象销毁前会自动调用析构,无需手动调用,而且只会调用一次
*构造和析构都必须有如果我们不提供,编译器自动提供一个空实现的构造和析构
#include<iostream>
#include<string>
using namespace std;
class person
{
public:
person() {
cout << "person的构造函数调用" << endl;
}
~person() {
cout << "person的析构函数调用" << endl;
}
};
void test01() {
person p;
}
int main() {
test01();
system("pause");
return 0;
}
2.2函数的分类以及调用
#include<iostream>
#include<string>
using namespace std;
class person
{
public:
int age;
//按参数分为:有参构造和无参构造(默认构造)
person() {
cout << "person的无参构造调用" << endl;
}
person(int a) {
age = a;
cout << "person的有参构造调用" << endl;
}
//按类型分为:拷贝构造和普通构造(有参构造和无参构造)、
person(const person &p) //相当于传入一个person到参数按照这个person拷贝一份
{
age = p.age;
cout << "person的拷贝构造调用" << endl;
}
};
void test01()
{ //括号法调用
person p1;/*无参构造调用 调用无参构造时不要加()
否则编译器会认为这是一个函数的声明不是在创建对象*/
person p2(10); //有参构造调用
person p3(p2); //拷贝构造调用
cout << "p2的年龄:" << p2.age << endl;
cout << "p3的年龄" << p3.age << endl; //对p2进行拷贝
}
int main() {
test01();
return 0;
}
2.2.1显示法和隐式转化法调用
#include<iostream>
#include<string>
using namespace std;
class person
{
public:
int age;
//按参数分为:有参构造和无参构造(默认构造)
person() {
cout << "person的无参构造调用" << endl;
}
person(int a) {
age = a;
cout << "person的有参构造调用" << endl;
}
//按类型分为:拷贝构造和普通构造(有参构造和无参构造)、
person(const person &p) //相当于传入一个person到参数按照这个person拷贝一份
{
age = p.age;
cout << "person的拷贝构造调用" << endl;
}
};
void test01()
{ //显示法调用
person p1;//无参构造调用
person p2= person(10); //有参构造调用
person p3=person (p2); //拷贝构造调用
cout << "p2的年龄:" << p2.age << endl;
cout << "p3的年龄" << p3.age << endl;
//隐式转化法
person p4 = 10; //相当于写了person p4= person(10)
person p5 = p4;
}
int main() {
test01();
return 0;
}
2.3拷贝构造函数调用时机
1.使用一个已经创建完毕的的对象来初始化一个新对象
#include<iostream>
#include<string>
using namespace std;
class person
{
public:
int age;
person() {
cout << "person的无参构造函数调用" << endl;
}
person(int a) {
age = a;
cout << "person的有参构造函数调用" << endl;
}
person(person &p) {
age = p.age;
cout << "person的拷贝构造函数调用" << endl;
}
~person() {
cout << "person的无参析构函数调用" << endl;
}
};
//1.使用一个已经创建完毕的的对象来初始化一个新对象
void test01() {
person p1(20);
person p2(p1);
cout << "p2的年龄为:" << p2.age << endl;
}
int main() {
test01();
system("pause");
return 0;
}
2..值传递的方式给函数参数传值 #include #include using namespace std; class person { public: int age; person() { cout << "person的无参构造函数调用" << endl; } person(int a) { age = a; cout << "person的有参构造函数调用" << endl; } person(person &p) { age = p.age; cout << "person的拷贝构造函数调用" << endl; }
~person() {
cout << "person的无参析构函数调用" << endl;
}
}; //2.值传递的方式给函数参数传值 void dowork(person p) {
} void test02() { person p; dowork(p); }
int main() { test02();
system("pause");
return 0;
}
#include<iostream>
#include<string>
using namespace std;
class person
{
public:
int age;
person() {
cout << "person的无参构造函数调用" << endl;
}
person(int a) {
age = a;
cout << "person的有参构造函数调用" << endl;
}
person(person &p) {
age = p.age;
cout << "person的拷贝构造函数调用" << endl;
}
~person() {
cout << "person的无参析构函数调用" << endl;
}
};
//3.值方式返回局部对象
person dowork2() {
person p1;
return p1;
}
void test03() {
person p = dowork2();//这里发生错误不懂问完再改
}
int main() {
test03();
system("pause");
return 0;
}
2.4构造函数调用规则
默认情况下,编译器至少给一类三 个函数 1.默认构造函数(无参,函数体为空) 2.默认析构函数(无参,函数体为空) 3.默认拷贝构造函数,对属性进行值拷贝
构造函数调用规则如下: 如果用户定义有参构造函数,则编译器不再提供默认无参构造,但会提供默认拷贝构造 如果用户定义拷贝构造函数,则编译器不再提供其他构造函数
2.5深拷贝和浅拷贝
浅拷贝:简单的赋值拷贝操作 深拷贝:在堆区重新申请空间,进行拷贝操作
浅拷贝:
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
int m_age;
int *m_height;
Person() {
cout << "Presond的默认构造函数调用" << endl;
}
Person(int age,int height) {
m_age = age;
m_height = new int(height);//用指针去接受堆区的数据
cout << "Presond的有参构造函数调用" << endl;
}
~Person() {
//析构代码将堆区开辟数据做释放操作
if (m_height != NULL /*(空的意思)*/) {
delete m_height;
m_height = NULL;//释放后防止野指针的出现将其置空
}
cout << "Presond的析构函数调用" << endl;
}
};
void test01() {
Person p1(18,180);
cout << "p1 age:" << p1.m_age<<"p1 height"<<p1.m_height << endl;
Person p2(p1);
cout << "p2 age:" << p2.m_age << "p2 height" << p2.m_height << endl;//我们没有提供拷贝构造函数,编译器做了浅拷贝的操作给我们提供了一个拷贝构造函数
}
int main() {
test01();
system("pause");
return 0;
}
运行截图:
浅拷贝问题:堆区内存重复释放
解决办法利用深拷贝开辟一块新的内存区域
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
int m_age;
int* m_height;
Person() {
m_height = nullptr;
cout << "Presond的默认构造函数调用" << endl;
}
Person(int age, int height) {
m_age = age;
m_height = new int(height);//用指针去接受堆区的数据
cout << "Presond的有参构造函数调用" << endl;
}
Person(const Person& p) {
m_age = p.m_age;
//m_height = p.m_height;编译器默认实现的就是这行代码
//进行深拷贝操作
m_height = new int(*p.m_height);
}
~Person() {
//析构代码将堆区开辟数据做释放操作
if (m_height != nullptr /*(空的意思)*/) {
delete m_height;
m_height = nullptr;//释放后防止野指针的出现将其置空
}
cout << "Presond的析构函数调用" << endl;
}
};
void test01() {
Person p1(18, 180);
cout << "p1 age:" << p1.m_age << "p1 height:" << *p1.m_height << endl;
Person p2(p1);
cout << "p2 age:" << p2.m_age << "p2 height:" << *p2.m_height << endl;
}
int main() {
test01();
system("pause");
return 0;
}
2.6初始化列表
作用:c++提供初始化列表语法,用于初始化属性
语法:构造函数():属性1(值1),属性1(值1),...{}
#include<iostream>
#include<string>
using namespace std;
class preson {
public:
preson(int a, int b, int c) :m_A(a), m_B(b),m_C(c){}
void printperson() {
cout << "a=" << m_A << endl;
cout << "b=" << m_B << endl;
cout << "c=" << m_C << endl;
}
private:
int m_A;
int m_B;
int m_C;
};
int main() {
preson p (20,30,40);
p.printperson();
system("pause");
return 0;
}
2.7类对象作为类成员
#include<string>
using namespace std;
//类对象作为类成员
class phone {
public:
phone(string PName)
{
m_PName = PName;
}
string m_PName; //手机品牌名称
};
class person {
public:
//phone m_PName = PName;(隐式转换法) 编译器自动利用PName给m_PName进行构造调用
person(string Name, string PName) :m_Name(Name), m_PName(PName) {
}
string m_Name;
phone m_PName;
};
void test() {
person p("张三", "华为");
cout << p.m_Name << "拿着" << p.m_PName.m_PName<<"手机" << endl;
}
int main() {
test();
system("pause");
return 0;
}
3 静态成员
静态成员就是在成员变量或者成员函数前加关键字static
静态成员分为:
3.1静态成员变量:
1.所以对象共享同一份数据
2.在编译阶段分配年存
3.类内声明类外初始化
#include<iostream>
#include<string>
using namespace std;
//类对象作为类成员
class person {
public:
static int m_A; //类内声明静态成员变量
};
int person::m_A = 10; //类外初始化
void test() {
//静态成员变量的两种访问方式
//1.通过对象
person p1;
p1.m_A = 100;
cout << "a=" << p1.m_A << endl;
person p2;
p2.m_A = 200;
cout << "2a=" << p2.m_A << endl;
cout << "a=" << p1.m_A << endl;//共享同一份数据
//static int m_A 只有一份内存空间,被所有 person 类的对象共享
//当通过 p1 修改 m_A 为 100 时,所有对象看到的 m_A 都是 100
//当通过 p2 修改 m_A 为 200 时,所有对象看到的 m_A 都变成了 200
//2.通过类名
cout << "m_A" << person::m_A << endl;
}
int main() {
test();
system("pause");
return 0;
}
3.2静态成员函数
1.所有对象共享同一个函数
2.静态成员函数只能访问静态成员变量
*类外访问不到私有静态成员变量
#include<iostream>
#include<string>
using namespace std;
class Person {
public:
static void func()//静态成员函数调用
{
m_A = 100;//静态成员函数可以访问静态成员变量,不可以访问非静态成员变量
cout << "static void func的调用" << endl;
}
static int m_A;//静态成员变量
};
int Person::m_A = 0;//静态成员类内声明类外初始化
void test01() {
//1.通过对象访问
Person p;
p.func();
//2.通过类名访问
Person::func();
}
int main() {
test01();
system("pause");
return 0;
}
4.c++对象模型和this指针
4.1成员变量和成员函数分开储存
#include<iostream>
#include<string>
using namespace std;
//成员变量和成员函数分开储存
class Person {
public:
int m_A;//非静态成员变量,属于类对象上
static int m_B;//静态成员变量,不属于类对象上
void func() {}; //非静态成员函数,不属于类对象上
static void func1() {};//静态成员函数,不属于类对象上
};
void test01() {
Person p;
cout << "size of p=" << sizeof(p) << endl; //空对象占用内存空间为1,因为编译器会给每个空对象分配一个字节空间,
//是为了区分空对象占内存的位置,每个空对象也应该有独一无二的内存地址
//
};
int main() {
test01();
system("pause");
return 0;
}
4.2 this指针概念