面向对象三大特性:封装、继承、多态
1. 封装
class 类名{ 权限 : 属性/行为}
类中的属性和行为统一称为成员。
属性 亦称为成员属性、成员变量;行为 亦称为成员行为
//设计一个圆类,求周长
const double PI = 3.14;
class Circle
{
public://公共权限
int m_r; //属性 用变量
double calZC() //行为 用函数
{
return 2*PI*m_r;
}
};
int main(){
Circle c1; //实例化一个对象
c1.m_r = 10; //给对象属性赋值
ZC = c1.calZC();
}
访问权限有三种:
public:类内类外都可访问
protected:类内可访问,类外不可访问,儿子可以访问父亲中的保护内容
private:类内可访问,类外不可访问,儿子不可以访问父亲中的私有内容
struct默认权限为public,class默认权限为private。
成员属性设为私有,但通过公共的行为进行属性修改,方便控制权限。
class student
{
public:
void setname(string name)
{
name = name;
}
string getname()
{
return name;
}
private:
string name; //wr
int age; //r
string idol; //w
};
int main()
{
student p;
// p.name = "张三" 会报错,因为name是私有
p.setname("张三");//但是可以通过公共行为对私有属性修改
//通过public的行为设置,可以实现读写权限的控制
return 0;
}
2. 初始化和清理
利用构造函数和析构函数进行初始化和清理。如果不提供,编译器会提供空实现。
战吼:构造函数 类名(){}
构造函数没有返回值;可以有参数,可重载;创建对象时会自动调用,且只调用一次。
亡语:析构函数 ~类名(){}
析构函数没有返回值;不能有参数,不可重载;对象销毁前会自动调用,且只调用一次。
class Person
{
Person(){...}//无参构造
Person(int age){m_age = age}//有参构造
Person(const Person &p){m_age = p.age}//拷贝构造 //将传入的对象所有属性拷贝到自己身上
};
int main()
{
Person p1;//创建对象后会执行无参构造函数。千万不要加括号,否则会认为是函数声明。
Person p2_1(10);//创建对象后会执行有参构造函数
Person p2_2 = Person(10);//效果和上一句一样
Person p2_3 = 10;//效果和上一句一样
Person p3_1(p2_1);//创建对象后会执行拷贝构造函数。且p3与p2相同。
Person p3_2 = Person(p2_2);//效果和上一句一样
Person p3_3 = p2_3;//效果和上一句一样
Person(10)//匿名对象 特点:当前行执行结束后,自动回收掉。
}
2.1 构造函数调用规则
默认情况下,C++编译器至少给一个类添加3个函数
- 默认构造函数,无参,函数体为空。
- 默认析构函数,无参,函数体为空。
- 默认拷贝构造函数,对属性进行拷贝。
调用规则:
- 如果用户定义有参构造函数,编译器不再提供默认无参构造,但是会提供默认拷贝构造。
- 如果用户定义拷贝构造函数,编译器不再提供其他构造函数。
2.2 深拷贝和浅拷贝(面试常考)
浅拷贝:简单赋值拷贝操作
深拷贝:在堆区重新申请空间,进行赋值
析构函数,可以将堆区开辟的数据做释放操作
class Person
{
public:
Person(){cout << "默认构造" << endl;}
Person(int age, int height)
{
m_age = age;
m_height = new int(height); //在堆区开辟空间
cout << "有参构造" << endl;
}
~Person()
{
if (m_height != NULL)
{
delete m_height;
m_height = NULL;
}
cout << "析构" << endl;
}
int m_age;
int* m_height;
};
上述对象,如果用Person p2(p1)//编译器提供的拷贝构造函数,进行浅拷贝,会在析构函数中重复释放内存,造成报错。
浅拷贝带来的问题是堆区内存重复释放。
所以要自己实现拷贝构造函数来解决问题:
Person(const Person &p)
{
m_age = p.age;
//编译器默认实现为:m_height = p.m_height
m_height = new int(*p.m_height);
}
2.3 初始化列表
构造函数(): 属性1(值1),属性2(值2), 属性3(值3);
class Person
{
public:
int m_a,m_b,m_c;
Person(int a, int b, int c):m_a(a),m_b(b),m_c(c){}
};
int main()
{
Person p(30,20,10);
}
3对象特性
3.1类对象作为类成员
套娃,类中的成员可以是另一个类。
class phone
{
public:
string p_number;
string p_type;
};
class person
{
public:
srting m_name;
phone m_phone;
};
实例化person时也会实例化phone。
顺序为:phone构造->person构造->person析构->phone析构。析构的顺序与构造相反。
3.2静态成员
使用static关键字
- 静态成员变量
(1) 所有对象共享同一份数据(同一个内存)
(2)编译时分配内存
(3)类内声明,类外初始化
class Person
{
public:
static int m_A;//类内声明
};
int Person::m_A = 100;//类外初始化
//静态成员变量不属于某个对象
int main(){
Person p1;
Person p2;
p2.m_A = 200;
cout << p1.m_A << p2.m_A << endl; // 通过对象访问。此时两个输出结果都是200
cout << Person::m_A << endl; //也可以通过类名访问
}
- 静态成员函数
(1)所有对象共享同一个函数
(2)静态成员函数只能访问静态成员变量
class Person
{
public:
static int m_A;
int m_B;
static void func()
{
m_A = 100;//合法
m_B = 100;//非法
}
};
int main()
{
//通过对象访问
Person p;
p.func();
//通过类名访问
Person::func();
}
类内成员对象和成员函数分开存储。只有非静态成员变量才属于类的对象上。
非静态成员变量,属于类的对象上;
静态成员变量,不属于类的对象上;
非静态成员函数,不属于类的对象上。
空类占用空间为1。
3.3 this指针
this指针指向被调用的成员函数所属对象。返回对象本身用*this
class Person
{
public:
int age;
Person(int age)
{
this->age = age;//this指针可解决
}
Person& PersonAddAge(Person &p)//返回值要加引用
{//this指向p2的指针,*this指向p2的本体。
this->age += p.age;
return * this;
}
}
int main()
{
Person p1(10);
Person p2(20);
p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
}
3.4const修饰成员函数
常函数内成员属性不可修改。(原理:this指针本身不可修改,加了个const之后连值也不能修改了)
但是成员属性声明时加mutable,可以修改。
常对象只能调用常函数。
class Person
{
public:
int m_A;
mutable int m_B;
void func01() const
{
m_A = 100; //这是非法的,因为是常函数,不能修改
m_B = 100; //这是合法的,因为m_B是mutable的
}
void func02(){}
}
int main()
{
const Person p; //常对象
p.m_A = 100; //这是非法的
p.m_B = 100; //这是合法的
p.func01(); //这是合法的
p.func02(); //这是非法的,常对象只能调用常函数
}
4.友元
4.1 全局函数做友元
在类中使用friend声明全局函数,可以使该函数访问该类的私有属性。
class building
{ //goodgay()函数是building的友元,可以访问building的私有成员
friend void goodgay(building* bd);
public:
building()
{
m_sittingroom = "客厅";
m_bedroom = "卧室";
}
string m_sittingroom;
private:
string m_bedroom;
};
void goodgay(building * bd)
{
cout << bd->m_sittingroom << endl;
cout << bd->m_bedroom << endl;//如果不用friend修饰,这句是非法的。
}
int main()
{
building bd;
goodgay(&bd);
return 0;
}
4.2 类做友元
class Building
{ //GoodGay类是building的友元,可以访问building的私有成员
friend class GoodGay;
public:
Building()
{
m_sittingroom = "客厅";
m_bedroom = "卧室";
}
string m_sittingroom;
private:
string m_bedroom;
};
class GoodGay
{
public:
GoodGay()
{
//创建一个Building
building = new Building;
}
Building* building;
void visit()//访问Building中的属性
{
cout << building->m_sittingroom << endl;
cout << building->m_bedroom << endl;
}
};
int main()
{
GoodGay gg;
gg.visit();
}
4.3 成员函数做友元
跑的程序有问题,暂时不写上来。