封装
一.对象的访问权限
默认的访问权限
1.struct默认为公开
2.class默认为私有
二.构造函数和析构函数
构造函数:初始化调用
类名{
}
析构函数:函数销毁时调用
~类名{
}
三.构造函数分类和调用
1.构造函数分类
-1.参数
有参构造
无参构造
-2.类型
普通构造
拷贝构造:需要传入一个相同类型的对象,复制这个对象的值到现有对象
2.构造函数调用
-1.括号法
Person p1;
Person p2(10);
Person p3(p2);
注意:默认构造不要加(),系统会认为是在声明
Person p1();
-2.显示法
Person p1;
Person p2=Person(10);
Person p3=Person(p2);
匿名对象
Person(10);
注意:不要用拷贝构造函数,初始化匿名对象,系统认为是Person(p3)===Person p3;
Person(p3);
3.隐式转换法
Person p4=10; //相当于Person p4=Person(10)
四.构造函数调用场景
1.使用已有对象初试化一个新对象
Person p3=Person(p2);
2.值传递的方式给函数参数传值
doWork(person p);
3.以值方式返回局部对象
Person doWork(){
Person p1;
return p1;
}
五.构造函数调用规则
1.默认函数
默认构造函数
默认析构函数
默认拷贝函数
2.用户定义有参构造,c++不会在提供默认无参构造,但会提供默认拷贝构造
3.用户定义拷贝构造函数,c++不会在提供其他构造函数
六.深拷贝和浅拷贝
1.浅拷贝
简单的复制拷贝操作
2.深拷贝
在兑取重新申请空间,进行拷贝操作
七.初始化列表属性(代替构造函数)
Person():m_A(10),m_B(20),m_C(30)
等价于
Person(int a,int b,int c){
this.a=a;
this.b=b;
this.c=c;
}
Person(a,b,c);
八.类对象成为类成员
1.将一个类作为属性创建在另一个类中
class A{}
class B{
A a;
}
2.顺序
A创建
B创建
B销毁
A销毁
九.静态成员
1.概念
静态成员就是在属性或函数前加static
2.特点
-静态成员变量
--所有对象共享同一份数据
--在编译阶段分配内存
--类内声明,类外初始化
-静态成员函数
--所有对象共享同一个函数
--静态成员函数只能访问静态成员变量
十.内存管理和this
1.类内成员变量和成员函数分开储存
只有非静态成员变量才属于类的对象上
2.this的用途
-1.因为在对象空间中,非静态变量会有多个实例化对象
利用this指向被调用的成员函数所属对象
-2.this是一个指针,*this 指向的是p2这个对象本体
十一.空指针调用成员函数
class Person
{
public:
void showage(){
cout<<""<<m_age;
}
int m_age;
}
void test(){
Person *p=NULL;
p->showage();
}
十二.const修士成员函数
常函数:
void showPerson() const{
}
相当于 const Person*this
左数右指
是一个常量指针,规定指向的值不可修改
1.成员函数后加const后我们称为这个函数为常函数
2.常函数内不可以修改成员属性
this的本质,指针常量,指针的指向不可修改,但指向的值可修改
3.成员属性声明是假关键词mutable后,在常函数中依然可以修改
常对象:
1.声明对象前加const成该对象为常对象
2.常对象只能调用常函数
十三.友元
实现访问私有变量
1.全局函数做友元
#include <iostream>
#include<string>
using namespace std;
class Building{
friend void ask(Building *a);
public:
Building(){
a="客厅";
b="卧室";
}
string a;
private:
string b;
};
void ask(Building *a){
cout<<a->b;
}
int main()
{
Building x;
ask(&x);
}
或者
void ask(Building &a){
cout<<a.b;
}
Building x;
ask(x);
引用的时候需要用.
指针用->
2.类做友元
friend class GoodGay;
3.成员函数做友元
friend void GoodGay::visit();
十四.运算符重载
1.加号运算符重载
实现两个自定义数据类型相加的运算
class Person
{
public:
int a;
int b;
};
//成员函数重载
Person operater+ (Person &p)
{
Person t;
t.a=this.a+p.a;
t.b=this.b+p.b;
return t;
}
Person p3=p1.operater(p2);
//等价于
Person p3=p1+p2;
//全局函数重载
#include <iostream>
#include<string>
using namespace std;
class Person
{
public:
int a;
int b;
};
Person operator+ (Person &p1,Person &p2)
{
Person t;
t.a=p1.a+p2.a;
t.b=p1.b+p2.b;
return t;
}
int main()
{
Person p1;
p1.a=10;
p1.b=10;
Person p2;
p2.a=10;
p2.b=10;
Person p3=p1+p2;
cout<<p3.a<<endl<<p3.b;
}
2.左移运算符重载
//成员函数重载
void operator<<(ostream&cout,Person &p){
cout<<p.a<<endl<<p.b;
}
//全局函数重载,需要实现友元,否则无法访问私有变量
ostream & operator<<(ostream&cout,Person &p){
cout<<p.a<<endl<<p.b;
return cout;
}
3.递增运算符重载
MyInteger& operator++(){
m_Num++;
return *this;
}
4.赋值运算符
Person& operator=(Person &p){
if(m_Age!=NULL){
delete m_Age;
m_Age=NULL;
}
m_Age=new int(*p.m_Age);
return *this;
}
5.关系运算符
bool operatpr==(Person &p){
if(this->m_name=p->m_name){
return ture;
}
return false;
}
6.函数调用运算符
void operator()(string test){
cout<<test<<endl;
}
-非常灵活,又叫仿函数
-可以实现各种功能
实例化对象
MyAdd myadd;
int ret =myadd(100);
匿名函数对象
MyAdd()(100,100);
继承
一.基础语法
class son:public father
{
public:
void content()
{
cout<<"对应内容";
}
};
二.继承方式
1.公共继承
2.保护继承
3.私有继承
三.继承中的对象模型
父类的私有类成员只是被隐藏了,但是还是被继承下来了
四.构造和析构顺序
1.父类构造
2.子类构造
3.子类析构
4.父类析构
五.访问同名成员
1.直接访问
s.a
s.func();
访问子类成员
2.加上作用域
s.Base::a
s.Base::func()
访问父类成员
六.访问同名静态成员
1.类内声明,类外赋值
2.实例化
Son s;
s.a
s.Base::a
s.func()
s.Base::func()
3.直接调用对象
Son::a
Son::Base::a
Son::Base::func()
七.多继承
class C:public A,public B
{
C c;
c.A::a;
c.B::a;
}
八.菱形继承
1.解决方式,通过虚继承解决
class Animal
{
public:
int a;
};
class Sheep: virtual public Animal{};
class Tuo: virtual public Animal{};
class SheepTuo:public Sheep,public Tuo{};
当我们使用虚继承时,SheepTuo的a只有一个,可以通过定义域修改,但是只看最后修改的值
两个指针,一个实际值
多态
一.静态多态和动态多态
1.静态多态
函数重载和运算符重载,复用函数名
2.动态多态
派生类和虚函数实现运行时多态
3.区别
静态多态的函数地址早绑定-编译
动态多态的函数地址晚绑定-运行
4.实例
//地址绑定
//倘若在animal的函数前加上virrus,那么结果就会变成 猫说话
#include<iostream>
using namespace std;
class Animal
{
public:
void speak()
{
cout<<"动物说话";
}
};
class Cat: public Animal
{
public:
void speak()
{
cout<<"猫说话";
}
};
void doSpeak(Animal &a)
{
a.speak();//输出 动物说话
}
int main()
{
Cat a;
doSpeak(a);
}
5.使用场景
-子类继承父类
-子类重写父类虚函数
二.多态原理剖析
1.虚函数表
重写覆盖虚函数表
二.纯虚函数和抽象类
1.因为父类的函数一般不会使用
所以将函数纯虚化
virtual void func()=0;
而纯虚函数所在的类又称为抽象类
2.限制
-无法实例化抽象类
-抽象类的子类,必须重写父类的纯虚函数,否则也无法实例化
三.虚析构和纯虚析构
1.多态使用时
如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
2.解决方式:
将父类的析构函数改为虚析构或者纯虚析构
因为delete的是animal,所以会跳过
将父类的虚构函数虚化
Animal::~Animal()=0;
#include<iostream>
#include<string>
using namespace std;
class Animal
{
public:
virtual void speak()
{
cout<<"动物说话";
}
};
class Cat: public Animal
{
public:
Cat(string name)
{
m_name=new string(name);
}
virtual void speak()
{
cout<<*m_name<<"猫说话";
}
~Cat()
{
if(m_name!=NULL)
{
delete m_name;
m_name=NULL;
}
}
string *m_name;
};
int main()
{
Cat a("Tom");
a.speak();
}
3.纯虚析构需要声明,也需要实现
有了纯虚析构以后,这个类也属于抽象类
文件操作
一.写文件
1.包含头文件
#include <fstream>
2.创建流对象
ofstream ofs
3.打开文件
ofs.open("address",打开方式);
ios::in 读文件
ios::out 写文件
ofs.open("stest.txt",ios::out);
4.写数据
ofs<<"";
5.关闭文件
ofs.close();
二.读文件
1.包含头文件
#include <fstream>
2.创建流对象
ofstream ifs
3.打开
ifs.open("stest.txt",ios::in);
4.读数据
-第一种
char buf[1024]={0};
while(ifs>>buf)
{
cout<<buf<<endl;
}
-第二种
char buf[1024]={0};
while(ifs.getline(buf,sizeof(buf)))
{
cout<<buf<<endl;
}
-第三种
char buf;
while(ifs.getline(buf))
{
cout<<buf<<endl;
}
-第四种
char c;
while((c=ifs.get())!=EOF)
{
cout<<c;
}
5.关闭文件
ifs.close();
三.二进制读文件
1.包含头文件
#include <fstream>
2.创建流对象
ofstream ofs
3.打开文件
ofs.open("address",打开方式);
ios::in 读文件
ios::out 写文件
ofs.open("stest.txt",ios::out|ios::binary);
4.写数据
Perosn p;
ofs<<"";
ofs.write((const char*)p);
5.关闭文件
ofs.close();
四.二进制写文件
1.包含头文件
#include <fstream>
2.创建流对象
ofstream ifs
3.打开
ifs.open("stest.txt",ios::in|ios::binary);
4.读数据
Perosn p;
ifs.read((char*)&p,sizeof(Person));
cout<<p.m_Name<<p.m_Age<<endl;
5.关闭文件
ifs.close();