C++基础知识

本文详细介绍了C++的基础知识,包括面向对象特性、类与结构体的区别、构造函数、拷贝构造与深浅拷贝、成员访问属性、初始化列表、虚函数、析构函数、友元和多态等核心概念。着重讨论了虚函数的实现原理和多态性,并通过实例展示了动态多态的调用机制。此外,还提到了静态成员、const修饰、操作符重载等相关知识点。
摘要由CSDN通过智能技术生成

C++基础知识(总结)

1…c++ 是面向对象的编程语言(考虑扩展维复用)
2.三大特性 : 封装 继承 多态。
3.C++ 类和结构体的区别,主要体现在默认访问属性上。
类默认访问属性为private,结构体默认访问属性为public。
3. 类 – 定义: 具有相同行为(函数 )和属性(成员变量 )的个体(对象)的抽象。类系统生成的默认无参构造是公有的.
3.1 类的成员的访问属性。public protected private 默认 --> private 外部不可见 只能在本类内可见
3.2 构造函数 函数名与类名一样 无返回值。种类分为无参构造,有参构造和拷贝构造。
系统默认生成的规则。当没有任何构造的时候(包括没有拷贝构造 ) , 系统会默认生成无参构造。
有任何的构造,包括拷贝构造,就没有系统默认生成的无参构造。
系统默认的拷贝构造。 只有自己写了拷贝构造系统才不会自动生成拷贝构造。否则系统会生成拷贝构造,并且是一个浅拷贝。
3.3 浅拷贝和深拷贝 , 主要说的是指针的成员.
浅拷贝就是只是对指针成员简单的赋值, 危害导致共用空间, 当回收空间时会多次释放而造成崩溃。
深拷贝指针成员并不是简单的赋值,而会分配独立空间。自己用自己的 , 不会出现多次释放的问题。
3.4 初始化列表 : 用来初始化成员。初始化引用成员 , 初始化const属性成员 , 还可以初始化父类, 以及初始化对象成员。

class A
A(){ cout ;}
{public:
};
class C
C(){ cout ;}
{
};
class B: public A
{
int b ; 
C c; // 对象成员
public: B() /* 隐含初始化 A() , c() */{ cout; } //隐含初始化 父类和对象成员
}; 定义 B b; 如果构造都有输出 , 那么输出就是 A C B
初始化列表是先于构造函数的。
class A
A(int val ): b(val) , a(b){} //初始化的顺序 和初始化列表里面写的先后顺序无关 , 与定义成员的顺序有关
int a; //先初始化a 然后是b
int b;
{public:
};
A a( 3); cout << a.a <<" " << a.b <<endl; //输出 不确定 , 3

虚表的虚指针是谁来赋值的? 是构造函数, 而不是初始化列表 --> 构造函数调用虚函数是一个静态联编, 只能调用自己的函数, 无法多态 – 构造函数内部不能实现本类与子类之间的多态
父类的构造早于子类的虚指针的赋值。

class A
virtual void init () { cout<<"A::init()";};
{public:
A() { init(); cout<< "A" ;} // init() 构造函数内调用 , 是静态联编 这个位置 子类的虚指针没有赋值, 所以调用父类
virtual void init () { cout<<"A::init()";};
};
class B :public A
virtual void init () { cout<<"B::init()";};
B() /* : A() */ { /* 虚指针赋值*/cout<< "B"};
{public:
};
B b; 输出 A::init() A B

拷贝构造: 当用一个类的对象初始化另一个类的对象时,调用拷贝构造。

A a;
A a1 = a; // 拷贝构造
A a;
A a1(a); // 拷贝构造
A a;
A a1;
a1 = a; // 赋值运算符函数

调用拷贝构造的常用的三个场景。
1.用一个类的对象初始化另一个类的对象。 2.形参是对象 , 给形参初始化 3. 返回值是对象, 给返回值初始化
都会调用什么函数?

class B;
B play( B b = bb )//2.调用拷贝构造
{
return b; //3.返回值的位置会给返回值拷贝构造
};//4.局部b对象要析构
执行语句
B bb ; // 1.调用B的构造
play( bb ); //5.该行结束, 会回收临时对象 , 这个位置会有一个析构
{
}//6.bb局部对象回收

explicit 关键字 使用explicit关键字修饰的构造函数, 可以避免不应该的隐式转换的发生。
3.5 析构函数 没有参数 没有返回值 ~类名 一般用来回收成员的 . 如果是父类, 最好写虚析构.
父类指针 = 子类对象;
回收时 delete 父类指针, 如果不写虚析构, 那么只会回收父类的空间 , 就会内存泄露
3.6 const属性 修饰成员属性, 修饰成员函数, 修饰对象 const在定义的时候要给初值.
const int p; --> p 不可变
int * const p; --> p不可变
const int
const p; --> p和
p都不可变 总结 就是 const 右边是谁 谁就不可变
const C中 是只读变量 C++ 就是常量 作用时期: 编译期有效, 用来做检查, 不可变. 是可以强制修改的.
C++下面

const int m = 4;
int * pm = (int*) &m;
*pm = 3;
cout << m << "," << *pm <<endl; //输出: 4, 3 m的值是常量表里面拿出来的 .

const的成员属性要在初始化列表里面给初值.
const修饰成员方法. this指针 是指向对象的地址
int A::GetA( /* const A* cosnt this /) const 常函数
return m_A; // 是不可以修改属性 因为隐藏参数 const A
A*不可变 所以无法修改属性. {}
const修饰对象 , 常对象 1.不可以改变属性 2. 只能调用常函数
const A a;
a.m_A = 3; 修饰对象 , 对象是不可以改变属性 , 常对象只能调用常函数.
普通对象是可以调用任何类内函数, 包括常函数 .
左值 右值
左值 是表示 能够在 = 左边和右边的表达式
右值 是表示 只能够在 = 右边的表达式
3.7 static属性 修饰成员属性 修饰成员方法 修饰对象 修饰类外的普通函数
修饰成员属性 : 放在全局区, 类共用一份, 不能被继承(共用关系 ) , 不依赖对象存在的. 要在类外初始化,
不能在.h中初始化 ,因为会重定义 需要在.cpp文件中初始化
修饰成员方法: 类共用一份, 不能被virtual 修饰, 因为共用, 不能直接调用非静态的成员属性 , 想要调用就要依赖对象, 只能直接调用静态成员.
因为他不依赖对象, 非静态成员他们再调用时依赖this指针, 也就是依赖对象.
对象是可以调用静态成员方法的.
class A
{
public: staic const int val = 0;
};
static 对象时和变量理解差不多, static 修饰全局变量 , 只能在当前的文件中, 作为全局变量使用, 生命周期是一直到程序结束
修饰的变量不支持extern static 修饰全局函数 , 只能在当前文件中使用。
3.8 inline属性
适用于代码少. 调用频繁的场合.在调用处展开, 是空间来换时间, 编译期有效。
virtual 虚函数可以和inline一起使用吗? 答案是不可以的. 因为虚函数时动态联编, 运行时有效. 不可以修饰同一个函数的.
3.9 virtual属性
用来修饰虚函数, 实现虚函数多态.
3.10 友元 friend 可以定义友元类和友元函数, 把自己对外不可见的函数或成员属性, 给你用

class B;
class A
{
public :
void Myprint() { B b; b.fun(); } //友元类可以访问该私有成员
};
class B
{
private: fun() {}
public:
friend class A;//友元类
friend void test() ;//友元函数
};
void test()
{
B b; b.fun(); //友元函数访问私有成员
}

总结: 友元, 可以使用类的私有和受保护成员, 但是友元函数一定不是类的成员函数, 想要使用成员, 必须依赖对象.
友元不能被继承 , 友元是破坏封装的, --> 私有属性 可以使用公有get方法。
3.11 纯虚函数 虚函数= 0 ; 含有纯虚函数的类叫抽象类, 全是纯虚函数的类叫接口类
性质: 含有纯虚函数的类, 不可以实例化对象的, 要求子类继承后, 完成了所有纯虚函数的实现, 子类才能定义对象.
3.12 操作符函数
可以重载操作符 CMyString 仿写Cstring operator = 赋值重载操作符 只能类内重载.
总结
空类默认生成的成员函数: 默认的无参构造, 析构, 拷贝构造, =运算符 , &运算符
不可以被virtual修饰(不能被继承的函数): 类外的普通函数 , inline , static , friend , 构造。

二,类之间的关系

横向的有组合 依赖 聚合 关联。纵向有继承。

  1. 继承 : 父类所有属性和方法复制一份给子类用。
    在这里插入图片描述
  2. 重载 重写(覆盖 ) 重写 override 可以了解一下
    重写 要求函数名参数,返回值一模一样。父类函数要有virtual修饰.
    隐藏
    区别 重载 , 相同作用域下 , 函数名一样参数列表(个数或者种类 )不一致。就是重载返回值没有要求。
  3. 多态 – 代码所具有多种形态。代码不变,功能可变。
    静态多态和动态多态。
    静态多态可以通过重载 , 或模板函数 ( 不是模板类 )来实现编译时有效。 ( 模板类是一个迟后编译 )
    动态多态一般也叫动态捆绑, 或动态联编。虚函数多态来实现。在运行时有效。
    3.1 虚函数多态的实现过程。
    1)父类中有虚函数。父类就有一张虚函数的虚表(虚函数的地址数组)。子类继承父类。子类也有一份儿虚函数的虚表。
    2)子类重写父类虚函数。子类的虚函数就会覆盖虚表中父类的虚函数地址.
    3)定义子类对象。在子类的前四个字节就会有一个虚指针。指向子类的虚函数表。父类指针指向子类对象。
    4)通过父类指针调用虚函数。那么就会根据子类的前四个字节, 找到虚表的入口地址。然后遍历虚表。找到对应的虚函数。
    完成函数调用。进而实现虚函数多态。
    为什么是运行时有效? 因为是取对象的前四个字节, 拿到虚指针. 对象的定义是在运行时才能确认地址.
    3.5虚函数例题
class A
{
public:
void AA(){}
virtual void BB() {}
 分区 0106 的第 15 页 
virtual void BB() {}
};
class B : public A
{
public : void AA() {}
void BB(){}
virtual void CC(){}
};
A*pa = new B;
pa->AA(); //调用 父类的AA
pa->BB(); // 调用 子类的BB

口诀: 调用函数的时候,首先看指针类型里边的函数是不是虚函数? 是虚函数, 找到对象前四个字节。看虚表中有没有重写。
不是虚函数, 调用该指针类型里面的函数。
虚函数多态的优缺点: 优点 : 代码不变 , 功能可变 . 缺点 , 效率低 , 破坏封装 , 父类无法调用子类特有的虚函数的 . (继承) 额外的空间开销

class A
{public:
void AA(){}
virtual void BB() {}
};
class B : public A
{
public : void AA() {}
void BB(){}
virtual void CC(){}
};
class KKK
{
public void BB(){}
};
class QQQ
{
public: virtual void BB(){}
}
A*pa = new B;
( (KKK*) pa ) -> BB(); //执行 : KKK :: BB();
( (QQQ*) pa ) -> BB(); //执行 : B :: BB(); //多态 调用虚表里面的
class A
{
public: virtual void AA(){}
};
class B : public A
{
public: virtual void AA override (){} // override 用于重写的检查
};
  自己实现虚表的函数的调用? 
class A
{public:
virtual void AA(){}
virtual void BB() {}
};
class B : public A
{
public: virtual void AA override (){} 
}; 
A *pa = new B; 假如 虚函数的类型 都是 virtual void AA();
typedef void (*PFUN)(); //PFUN 函数指针类型
// 思路 : 根据对象的前四个字节 拿到虚指针, 找到虚表的入口地址. 然后调用虚函数. 把虚函数地址当成一个四字节数处理
取前四个字节 *(int*)pa ; 虚表的首元素地址 数组 转化为数组 因为地址就是四个字节 我们借用int来存储
int * arr = (int *) *(int*)pa; // arr 是一个数组 --> 虚表数组 虚表的首地址 arr 
PFUN AA = (PFUN)arr[0]; //虚表的第一个元素 函数地址
AA(); //B:AA
PFUN BB = (PFUN)arr[1]; //虚表的第二个元素 函数地址
BB(); // A::BB
特殊多态: 使用引来来多态
B b;
A & a = b;
a->AA() ; //可以触发多态
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值