文章目录
面向对象
对象,就是对问题中的事物的抽象
面向对象:就是把现实中的事物都抽象为“对象”。每个对象是唯一的,且都可以拥有它的属性与行为。我们就可以通过调用这些对象的方法、属性去解决问题。
-
对象
对象是由数据(描述事物的属性)和作用于数据的操作(体现事物的行为)组成的封装体,描述客观事物的一个实体,是构成系统的基本单元。
-
类
类是对一组有相同数据和相同操作的对象的定义,是对象的模板,其包含的方法和数据描述一组对象的共同行为和属性。类是在对象之上的抽象,对象则是类的具体化,是类的实例。类可有其子类,也可有其他类,形成类层次结构。
-
类是对象的抽象,而对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,占用存储空间
面向对象程序设计(OOP)的核心思想是数据抽象、 继承和动态绑定。
- 使用数据抽象, 我们可以将类的接口与实现分离(封装)。
- 使用继承, 我们可以定义相似的类型并对其相似关系建模。
- 使用动态绑定, 可以在一定程度上忽略相似类型的区别, 而以统一的方式使用它们的对象(多态)
面向对象的基本特征
(76条消息) 什么是面向对象,它的三个基本特征:封装、继承、多态_面向对象的三个基本特征_冰棍hfv的博客-CSDN博客
[(86条消息) 【C++ 语言】面向对象 ( 继承 | 重写 | 子类调用父类方法 | 静态多态 | 动态多态 | 虚函数 | 纯虚函数 )_c++ 子类重写父类方法_韩曙亮的博客-CSDN博客](https://blog.csdn.net/shulianghan/article/details/99690475#:~:text=%2F%2F声明 Parent1 父类对象%2C 为其赋值一个 Child 对象 Parent1* parent1,%3D new Child()%3B %2F%2F此时调用其 parent_method 方法%2C 调用的是父类的方法 parent1->parent_method()%3B)
封装
封装(encapsulation)即信息隐蔽。它是指在确定系统的某一部分内容时,应考虑到其它部分的信息及联系都在这一部分的内部进行,外部各部分之间的信息联系应尽可能的少。
public/private访问范围
-
用户代码(类外)可以访问public成员而不能访问private成员;private成员只能由类成员(类内)和友元访问。
-
protected成员可以被**子类对象(也称为派生类)**访问,不能被用户代码(类外)访问。
-
继承后改变访问属性
**1.public继承:**基类public成员,protected成员,private成员的访问属性在派生类中分别变成:public, protected, private
**2.protected继承:**基类public成员,protected成员,private成员的访问属性在派生类中分别变成:protected, protected, private
**3.private继承:**基类public成员,protected成员,private成员的访问属性在派生类中分别变成:private, private, private
友元函数/友元类
(1)友元函数:在定义一个类的时候,可以把一些函数(包括全局函数和其他类的成员函数)声明为“友元”,这样那些函数就成为该类的友元函数,在友元函数内部就可以访问该类对象的私有成员了
-
将全局函数声明为友元的写法如下:
friend int MostExpensiveCar(CCar cars[], int total); //声明友元
-
将其他类的成员函数声明为友元的写法如下:
friend void CDriver::ModifyCar(CCar* pCar); //声明友元
但是,不能把其他类的私有成员函数声明为友元。
(2)友元类:一个类 A 可以将另一个类 B 声明为自己的友元,类 B 的所有成员函数就都可以访问类 A 对象的私有成员。在类定义中声明友元类的写法如下:
class CCar
{
private:
int price;
friend class CDriver; //声明 CDriver 为友元类
};
class CDriver
{
public:
CCar myCar;
void ModifyCar() //改装汽车
{
myCar.price += 1000; //因CDriver是CCar的友元类,故此处可以访问其私有成员
}
};
继承(类的继承)
让某个类型的对象获得另一个类型的对象的属性和方法。继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
-
继承表示 : C++ 中继承可以使用 “:” 符号 , 格式为 “class 子类名称 : 父类名称{};”
//父类 class Parent{}; //子类 //继承父类 Parent class Child : Parent{};
-
继承作用域 : 继承可以指定作用域 , private , protected , public , 如果不写作用域 , 那么该继承就是默认私有继承 ; 作用域符号写在 冒号之后 , 父类名称之前 ;
-
私有 ( private ) 继承 : 如果继承的作用域是私有的 ( private ) , 那么继承的所有的方法都是私有的 , Parent 中的 public 和 protected 方法会变成私有的 , 外部无法访问该方法 ;
-
C++ 多继承 : Java 中只能进行单继承 , 但是在 C++ 中是可以继承多个父类的 ; 在多继承时 , 使用 “,” 将多个父类分隔即可 ;多继承中 , 每个父类的作用域都可以单独指定;
//子类 //继承父类 Parent //继承作用域 : 默认私有继承 private class Child : private Parent, public Parent1{};
-
子类重写父类方法 : 如果继承的两个父类有相同的方法 , 在子类又定义了一个相同的方法 , 这就是重写父类的方法 ;
-
调用子类重写的 “parent_method” 方法 , 其执行的是子类重写的方法 ,
外部通过子类调用父类方法 : 如果调用的方法在子类中没有重写 , 那么调用的就是父类的方法
-
//父类 class Parent1 { public: void parent_method() { cout << " Parent1 parent_method " << endl; } void parent_method1() { cout << " Parent1 parent_method1 " << endl; } }; //子类 //继承父类 Parent //继承作用域 : 默认私有继承 private class Child : private Parent, public Parent1 { public: void parent_method() { cout << " Child parent_method " << endl; } }; //在栈内存中创建一个 Child 对象, 并调用其重写的父类的方法 Child child; //调用子类重写的父类的方法 结果:child parentmethod child.parent_method(); //调用子类没有重写的父类方法 结果:parent1 parentmethod1 child.parent_method1();
- 子类中调用父类的方法 : 使用 " 父类名称 :: 方法名() " 进行调用
class Child : private Parent, public Parent1 {
public:
void parent_method() {
//子类中调用父类的方法 , 该操作相当于 Java 中的 super 方法
Parent::parent_method();
Parent1::parent_method();
cout << " Child parent_method " << endl;
}
};
//在栈内存中创建一个 Child 对象, 并调用其重写的父类的方法
Child child;
//调用子类重写的父类的方法 结果为:
/*Parent parent_method
Parent1 parent_method
Child parent_method*/
child.parent_method();
多态
声明 Parent1 父类对象 , 为其赋值一个 Child 对象 , 此时调用其 parent_method 方法 , 调用的是父类的方法 ;
//声明 Parent1 父类对象 , 为其赋值一个 Child 对象
Parent1* parent1 = new Child();
//此时调用其 parent_method 方法 , 调用的是父类的方法,即结果为输出parent...
parent1->parent_method();
1. 虚函数/重载:
如果必须要调用子类实现的方法 , 这里就需要用到虚函数
new/delete
-
new是CPP定义的关键字,不是函数,通过特定的语法可以组成表达式。和 sizeof 不同的是,sizeof 在编译时候就可以确定其返回值。
-
基本用法:
int *a = new int[5]; delete []a; // class A { public: A(int v) : var(v) { fopen_s(&file, "test", "r"); } ~A() { fclose(file); } private: int var; FILE *file; }; A *obj = new A(10); //使用 new 创建对象 delete obj;
-
例子中new关键字的具体工作方式:
- (new)调用下面提到的 operator new 标准库函数,传入的参数为 class A 的大小,这里为 8 个字节,至于为什么是 8 个字节,你可以看看《深入 C++ 对象模型》一书,这样函数返回的是分配内存的起始地址,这里假设是 0x007da290。
- ( A(10) )上面分配的内存是未初始化的,也是未类型化的,第二步就在这一块原始的内存上对类对象进行初始化,调用的是相应的构造函数,即调用
A:A(10);
这个函数,从图中也可以看到对这块申请的内存进行了初始化,var=10, file 指向打开的文件
。 - (A* obj = )最后一步就是返回新分配并构造好的对象的指针,这里 obj就指向 0x007da290 这块内存,obj的类型为类 A 对象的指针。
-
-
operator new是cpp的标准库库函数,不是operator重载符,用于分配空间
缺省情况下,C++在global作用域内提供以下形式的operator new:
void *operator new(size_t); //allocate an object void *operator delete(void *); //free an object void *operator new[](size_t); //allocate an array void *operator delete[](void *); //free an array
原文链接:https://blog.csdn.net/zqxf123456789/article/details/108431315
-
如果定义了自定义的operator new/placement new成员函数,一定要对应的定义与之匹配的operator delete/placement delete。
由于成员函数的名称会掩盖其外围作用域的相同名称,所以如果自定义了operator/placement new,那么标准库版本的operator new就会被掩盖而无法调用。例如:
Employee { public: static void* operator new(std::size_t size, std::ostream& logStream) throw(std::bad_alloc); //这个new会掩盖正常的global形式 ... }; Employee* pb = new Employee; //Error! 正常形式的operator new被掩盖 Employee* pb = new (std::cerr) Employee; //Correct, 调用Employee的placement new
-
命名空间(namespace)
C++中名称(name)可以是符号常量、变量、函数、结构、枚举、类和对象
变量、函数和类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染。命名空间只能全局范围内定义,且可以重定义编译器最终会归类到一起
-
命名空间里可以定义变量,也可以定义函数。
访问a时需要加上域作用限定符(::),即N1::anamespace N1 { int a = 10;//定义变量 int add(int left,int right)//定义函数 { return left+right; } namespace N2{class X;}//可以嵌套,调用时使用N1::N2::X } void N1::func()//成员函数 在外部定义的时候 记得加作用域 { //访问命名空间的数据不用加作用域 cout<<"func遍历a = "<<a<<endl; }
-
使用using namespace引入命名空间;也可以using N1::a引入命名成员
例如using namespace std
类与接口
堆与栈
临时变量
引用类型
GC