C++面向对象三大特性:封装、继承、多态
三种访问权限:
- public: 公有权限 成员类内和类外均可以访问
- protected:保护权限,类外不可以访问,子类成员可以访问基类的保护数据成员
- private:私有成员,成员类内可以访问,类外不可以访问
class Person
{
//姓名 公共权限
public:
string m_Name;
//汽车 保护权限
protected:
string m_Car;
//银行卡密码 私有权限
private:
int m_Password;
public:
void func()
{
m_Name = "张三";
m_Car = "拖拉机";
m_Password = 123456;
}
};
int main() {
Person p;
p.m_Name = "李四";
//p.m_Car = "奔驰"; //保护权限类外访问不到
//p.m_Password = 123; //私有权限类外访问不到
system("pause");
return 0;
}
类和结构体唯一区别:默认的访问权限不同
- struct 默认权限是公共
- class 默认权限是私有
c++编译器至少给一个类添加4个函数
- 1. 默认构造函数(无参,函数体为空)
- 2. 默认析构函数(无参,函数体为空)
- 3. 默认拷贝构造函数,对属性进行值拷贝
- 4. 赋值运算符 operator=, 对属性进行值拷贝
C++利用构造函数和析构函数来解决类中数据成员初始化和类对象的清理问题;
构造函数归纳与总结
注意:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题;
静态成员:必须在类内声明,然后在类外进行初始化。
静态成员变量不属于某个对象上,所有对象都共享同一份数据;
class Person
{
public:
static int m_A; //静态成员变量
//静态成员变量特点:
//1 在编译阶段分配内存
//2 类内声明,类外初始化
//3 所有对象共享同一份数据
private:
static int m_B; //静态成员变量也是有访问权限的
};
//静态成员变量在类外进行初始化
int Person::m_A = 10;
int Person::m_B = 10;
void test01()
{
//静态成员变量两种访问方式
//1、通过对象
Person p1;
p1.m_A = 100;
cout << "p1.m_A = " << p1.m_A << endl;
Person p2;
p2.m_A = 200;
cout << "p1.m_A = " << p1.m_A << endl; //共享同一份数据
cout << "p2.m_A = " << p2.m_A << endl;
//2、通过类名
cout << "m_A = " << Person::m_A << endl;
//cout << "m_B = " << Person::m_B << endl; //私有权限访问不到
}
int main() {
test01();
system("pause");
return 0;
}
静态成员函数只可以访问静态成员变量,而其他变量访问不到
class Person
{
public:
//静态成员函数特点:
//1 程序共享一个函数
//2 静态成员函数只能访问静态成员变量
static void func()
{
cout << "func调用" << endl;
m_A = 100;
//m_B = 100; //错误,不可以访问非静态成员变量
}
static int m_A; //静态成员变量
int m_B; //
private:
//静态成员函数也是有访问权限的
static void func2()
{
cout << "func2调用" << endl;
}
};
int Person::m_A = 10;
void test01()
{
//静态成员变量两种访问方式
//1、通过对象
Person p1;
p1.func();
//2、通过类名
Person::func();
//Person::func2(); //私有权限访问不到
}
int main() {
test01();
system("pause");
return 0;
}
const修饰成员函数
常函数:
- 成员函数后加const后我们称为这个函数为**常函数**
- 常函数内不可以修改成员属性
- 成员属性声明时加关键字mutable后,在常函数中依然可以修改
常对象:
- 声明对象前加const称该对象为常对象
- 常对象只能调用常函数
友元的三种实现
- 全局函数做友元 friend void goodgay(Building *building);
- 类做友元 friend class goodgay;
- 成员函数做友元 friend void goodgay::visit();
运算符重载
运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
重载+、<<运算符
class Person {
public:
Person() {};
Person(int a, int b)
{
this->m_A = a;
this->m_B = b;
}
//成员函数实现 + 号运算符重载
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;
}
public:
int m_A;
int m_B;
};
//全局函数实现 + 号运算符重载
//Person operator+(const Person& p1, const Person& p2) {
// Person temp(0, 0);
// temp.m_A = p1.m_A + p2.m_A;
// temp.m_B = p1.m_B + p2.m_B;
// return temp;
//}
//运算符重载 可以发生函数重载
Person operator+(const Person& p2, int val)
{
Person temp;
temp.m_A = p2.m_A + val;
temp.m_B = p2.m_B + val;
return temp;
}
void test() {
Person p1(10, 10);
Person p2(20, 20);
//成员函数方式
Person p3 = p2 + p1; //相当于 p2.operaor+(p1)
cout << "mA:" << p3.m_A << " mB:" << p3.m_B << endl;
Person p4 = p3 + 10; //相当于 operator+(p3,10)
cout << "mA:" << p4.m_A << " mB:" << p4.m_B << endl;
}
int main() {
test();
system("pause");
return 0;
}
重载左移运算符: friend ostream& operator<<(ostream& out, Person& p);
ostream& operator<<(ostream& out, Person& p)
{
out << "a:" << p.m_A << " b:" << p.m_B;
return out;
}
重载赋值运算符:person & operator=(person &p);
class Person
{
public:
Person(int age)
{
//将年龄数据开辟到堆区
m_Age = new int(age);
}
//重载赋值运算符
Person& operator=(Person &p)
{
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
//编译器提供的代码是浅拷贝
//m_Age = p.m_Age;
//提供深拷贝 解决浅拷贝的问题
m_Age = new int(*p.m_Age);
//返回自身
return *this;
}
~Person()
{
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
}
//年龄的指针
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();
//int a = 10;
//int b = 20;
//int c = 30;
//c = b = a;
//cout << "a = " << a << endl;
//cout << "b = " << b << endl;
//cout << "c = " << c << endl;
system("pause");
return 0;
}
重载关系运算符:bool operator==(person &p);
重载关系运算符,可以让两个自定义类型对象进行对比操作
class Person
{
public:
string m_Name;
int m_Age;
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
};
bool operator==(Person & p)
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
{
return true;
}
else
{
return false;
}
}
bool operator!=(Person & p)
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
{
return false;
}
else
{
return true;
}
}
};
void test01()
{
//int a = 0;
//int b = 0;
Person a("孙悟空", 18);
Person b("孙悟空", 18);
if (a == b)
{
cout << "a和b相等" << endl;
}
else
{
cout << "a和b不相等" << endl;
}
if (a != b)
{
cout << "a和b不相等" << endl;
}
else
{
cout << "a和b相等" << endl;
}
}
int main() {
test01();
system("pause");
return 0;
}
重载函数调用运算符():仿函数
例如: int operator()(int v1, int v2);
class MyPrint
{
public:
void operator()(string text)
{
cout << text << endl;
}
};
void test01()
{
//重载的()操作符 也称为仿函数
MyPrint myFunc;
myFunc("hello world");
}
class MyAdd
{
public:
int operator()(int v1, int v2)
{
return v1 + v2;
}
};
void test02()
{
MyAdd add;
int ret = add(10, 10);
cout << "ret = " << ret << endl;
//匿名对象调用
cout << "MyAdd()(100,100) = " << MyAdd()(100, 100) << endl;
}
int main() {
test01();
test02();
system("pause");
return 0;
}
类的继承
继承的语法: class 子类:继承方式 父类
结构:class A:public B;
A类称为子类或者派生类
B类称为父类或基类
重点概念:
父类中所有的非静态成员属性都会被子类继承下去;父类中的私有成员属性是被编译器隐藏了,所以子类对象访问不到,但却是是被继承下去了;
class Base
{
public:
int m_A;
protected:
int m_B;
private:
int m_C; //私有成员只是被隐藏了,但是还是会继承下去
};
//公共继承
class Son :public Base
{
public:
int m_D;
};
void test01()
{
cout << "sizeof Son = " << sizeof(Son) << endl;
//16,父类中的所有成员,不管是什么样的属性或什么样的权限,子类都会继承下来保留一份,然后再加上自身的那一份;
}
int main() {
test01();
system("pause");
return 0;
}
继承中构造函数与析构函数调用顺序
#include <iostream>
using namespace std;
class base
{
public:
base()
{
cout << "基类构造函数的调用!\n";
}
~base()
{
cout << "基类析构函数的调用!\n";
}
};
class son :public base
{
public:
son()
{
cout << "子类构造函数的调用!\n";
}
~son()
{
cout << "子类析构函数的调用!\n";
}
};
void test01()
{
son s;
}
int main()
{
test01();
system("pause");
return 0;
}
运行结果:
总结:先构造父类,再构造子类,析构顺序与构造顺序恰好相反
当子类与父类出现同名的成员,如何通过子类对象,访问子类或父类中同名的数据?
- 访问子类同名成员 直接访问即可:如 s.m_A
- 访问父类同名成员 需要加作用域: 如 s.Base::m_A