当前位置: 首页 > news >正文

【C++】复习重点-汇总2-面向对象(三大特性、类/对象、构造函数、继承与派生、多态、抽象类、this/对象指针、友元、运算符重载、static、类/结构体)

面向对象

面向对象编程(Object-Oriented Programming,OOP)是 C++ 的核心特性之一。它提供了一种以“类”和“对象”为基础的编程范式,强调数据抽象、模块化、继承和多态等机制。

一、面向对象三大特性(核心)

特性说明关键字
封装把数据和操作打包到一个类中,隐藏实现细节private, public, protected
继承子类继承父类成员,代码复用class B : public A
多态相同接口表现不同行为,主要通过虚函数实现virtual, override, = 0

封装 是将数据成员和操作这些数据的函数绑定在一个类中,对外隐藏实现细节,仅暴露必要接口。它提升了模块独立性、安全性和代码可维护性。

class Account {
private:double balance;public:void deposit(double amount);double getBalance() const;
};

继承 支持创建新的类(派生类),在保留已有类(基类)特性的同时扩展新功能,从而实现代码复用和逻辑扩展。

class Person {
public:void walk();
};class Student : public Person {
public:void study();
};

多态 是指在同一接口下,表现出不同的行为形式,分为:

  • 编译时多态(静态多态):通过函数重载、运算符重载实现
  • 运行时多态(动态多态):通过虚函数和基类指针/引用调用派生类函数实现

二、类与对象基础

知识点简要说明示例关键字
类与对象类是模板,对象是实例class, object
构造函数创建对象时自动调用constructor
析构函数对象销毁前自动调用~ClassName()
成员函数定义在类中的函数void func()
成员变量类中的变量int a;

1.类

类是一种用户自定义的数据类型,定义对象的属性(成员变量)与行为(成员函数)。
类的声明:

class 类名
{public:公有数据成员;公有成员函数;private:私有数据成员;私有成员函数;protected:保护数据成员;保护成员函数;
};

2.对象

类的实例,使用类定义的结构在内存中创建实际实体。

//1.声明类同时定义对象
class 类名
{类体;
}对象名列表;
//2.先声明类,再定义对象
类名 对象名(参数列表);//参数列表为空时,()可以不写
//3. 不出现类名,直接定义对象
class 
{类体;
}对象名列表;
//4.在堆上创建对象
Person p(123, "yar");//在栈上创建对象
Person *pp = new Person(234,"yar");//在堆上创建对象

对象成员的引用:
对象名.数据成员名 或者 对象名.成员函数名(参数列表)
注:不可以在定义类的同时对其数据成员进行初始化,因为类不是一个实体,不合法但是能编译运行

三、访问控制(权限修饰)

关键字含义
public公共成员,类外可访问
private私有成员,仅类内可访问
protected受保护成员,类内和子类可访问

和Java、C#不同的是,C++中public、private、protected只能修饰类的成员,不能修饰类,C++中的类没有共有私有之分
类内部没有访问权限的限制,都可以互相访问。
在C++中用class定义的类中,其成员的默认存取权限是private。
访问控制符限定类成员对外部访问权限:

访问修饰符类内访问派生类访问类外访问
public
protected
private

四、构造函数

类型描述
默认构造函数无参数构造
有参构造函数带参数构造
拷贝构造函数用一个对象创建另一个对象
析构函数对象销毁时自动调用,释放资源
构造函数初始化列表用于成员变量初始化,性能更好

构造函数:在对象创建时自动调用,用于初始化成员变量。可重载。
析构函数:对象生命周期结束时自动调用,用于释放资源。只能定义一个,且无参数。

这里要区分 函数声明,函数定义,构造函数,我看到这里感觉构造函数与声明挺像的,所以进行了对比。

比较项函数声明函数定义构造函数
是否属于类可是类内成员,也可以是类外函数同上是类的专属成员函数
作用告诉编译器函数的原型提供函数的具体实现创建对象时自动执行初始化
是否含函数体❌ 无函数体✅ 有函数体✅ 有函数体
名称规则任意合法名称与声明一致必须与类名相同
返回值有(如 int、void)❌ 无(连 void 也不能写)
调用方式手动调用,如 add(1, 2)同上自动调用,如 Person p;
是否可以重载✅ 可声明多个重载版本✅ 函数体可重载✅ 支持多个参数列表重载
常见位置头文件或函数体前源文件或类中类体内或外部定义
特殊性普通语法结构普通语法结构C++ 面向对象特性

函数声明是“告诉编译器函数长什么样”,函数定义是“告诉它怎么做”,而构造函数是“在类对象创建时自动做初始化”的专属函数,它不写返回值、名字和类名相同。

/////////////// 对比 ////////////////////
int add(int a, int b);  // 只有函数签名,无实现int add(int a, int b) {  // 含函数体return a + b;
}class Person {
public:Person(string name) { //构造函数cout << "Hello, " << name << endl;}
};
////////////////析构 构造 ///////////////////
class File {
public:File();             // 构造函数~File();            // 析构函数
};
///////////////// 带默认参数的构造函数 //////////////////
class Person{
public:Person(int = 0,string = "张三");void show();
private:int age;string name;
};
// 带默认参数的构造函数
Person::Person(int a, string s){cout<<a<<" "<<s<<endl;age = a;name = s;
}
//带参数初始化表的构造函数
Person::Person(int a, string s):age(a),name(s){cout << a << " " << s << endl;
}
void Person::show(){cout << "age="<<age << endl;cout << "name=" <<name << endl;
}
int main(){Person p; //0 张三Person p2(12);//12 张三Person p3(123, "yar");//123 yarreturn 0;
}

构造函数重载

class Person{
public:Person();Person(int = 0,string = "张三");Person(double,string);void show();
private:int age;double height;string name;
};
...

拷贝构造函数

类名::类名(类名&对象名)
{函数体;
}
class Person
{
public:Person(Person &p);//声明拷贝构造函数Person(int = 0,string = "张三");void show();
private:int age;string name;
};
Person::Person(Person &p)//定义拷贝构造函数
{cout << "拷贝构造函数" << endl;age = 0;name = "ABC";
}
Person::Person(int a, string s):age(a),name(s)
{cout << a << " " << s << endl;
}
int main()
{Person p(123, "yar");Person p2(p);p2.show();return 0;
}
//输出
123 yar
拷贝构造函数
age=0
name=ABC

五、继承与派生类

1.继承与派生GN

继承:在一个已有类的基础上建立一个新类,已有的类称基类或父类,新类称为派生类和子类;
派生:GN同继承,角度不同,继承是儿子继承父亲的产业,派生是父亲把产业传承给儿子。
一个基类可以派生出多个派生类,一个派生类可以继承多个基类(n*n多对多)

类型说明示例
单继承一个子类继承一个父类class B : public A {}
多继承一个类继承多个父类class C : public A, B {}
虚继承用于解决菱形继承问题class D : virtual public A
class 派生类名 : 继承方式 基类名 {// 派生类的新成员变量和成员函数
};
常见继承方式
public(公有继承) 最常用
protected(保护继承)
private(私有继承)默认

公有继承

#include <iostream>
using namespace std;
// 基类
class Animal {
public:void eat() {cout << "Animal is eating." << endl;}
};// 派生类
class Dog : public Animal {
public:void bark() {cout << "Dog is barking." << endl;}
};int main() {Dog d;d.eat();   // 继承自 Animald.bark();  // 自己的成员函数return 0;
}
//Animal is eating.
//Dog is barking.
继承方式基类 public 成员在派生类中变成protected 成员private 成员
publicpublicprotected不可访问
protectedprotectedprotected不可访问
privateprivateprivate不可访问

利用using关键字可以改变基类成员再派生类中的访问权限;
using只能修改基类中public和protected成员的访问权限。

class Base
{
public:void show();
protected:int aa;double dd;
};
void Base::show(){
}
class Person:public Base
{
public:using Base::aa;//将基类的protected成员变成publicusing Base::dd;//将基类的protected成员变成public
private:using Base::show;//将基类的public成员变成privatestring name;
};
int main()
{Person *p = new Person();p->aa = 12;p->dd = 12.3;p->show();//出错delete p;return 0;
}

2.构造函数与析构函数的调用顺序

构造顺序:先构造基类 → 再构造派生类
析构顺序:先析构派生类 → 再析构基类

#include <iostream>
using namespace std;// 基类
class Base {
public:Base() {cout << "Base 构造函数被调用" << endl;}~Base() {cout << "Base 析构函数被调用" << endl;}
};// 派生类
class Derived : public Base {
public:Derived() {cout << "Derived 构造函数被调用" << endl;}~Derived() {cout << "Derived 析构函数被调用" << endl;}
};int main() {cout << "main 开始执行" << endl;Derived obj;cout << "main 结束执行" << endl;return 0;
}

main 开始执行
Base 构造函数被调用
Derived 构造函数被调用
main 结束执行
Derived 析构函数被调用
Base 析构函数被调用

六、多态(Polymorphism)

静态多态:

  • 函数重载(Overload)
  • 运算符重载(Operator Overload)

动态多态:

  • 使用虚函数 virtual
  • 需要基类指针/引用指向派生类对象
  • 支持运行时多态(Late Binding)
#include <iostream>
using namespace std;class Animal {
public:virtual void speak() {  // 虚函数cout << "Animal speaks" << endl;}
};class Dog : public Animal {
public:void speak() override {  // 重写父类方法cout << "Dog barks" << endl;}
};void makeSound(Animal* animal) {animal->speak();  // 运行时绑定
}int main() {Dog d;makeSound(&d);  // 输出:Dog barks(多态)return 0;
}

3.多继承

多继承容易让代码逻辑复杂、思路混乱,一直备受争议,中小型项目中较少使用,后来的 Java、C#、PHP 等干脆取消了多继承。

构造与析构顺序说明:

  • 构造顺序:
    按照继承列表中声明的顺序依次调用 A() → B() → C()。

  • 析构顺序:
    先析构 C(),再反向析构 B() → A()。

#include <iostream>
using namespace std;// 基类 A
class A {
public:A() {cout << "A 构造函数被调用" << endl;}~A() {cout << "A 析构函数被调用" << endl;}void showA() {cout << "A::showA()" << endl;}
};// 基类 B
class B {
public:B() { cout << "B 构造函数被调用" << endl; }~B() {cout << "B 析构函数被调用" << endl;}void showB() { cout << "B::showB()" << endl;}
};// 派生类 C,同时继承 A 和 B
class C : public A, public B {
public:C() { cout << "C 构造函数被调用" << endl;  }~C() { cout << "C 析构函数被调用" << endl; }void showC() { cout << "C::showC()" << endl;}
};
int main() {cout << "main 开始执行" << endl;C obj;obj.showA();  // 访问来自 A 的函数obj.showB();  // 访问来自 B 的函数obj.showC();  // 访问自己的函数cout << "main 结束执行" << endl;return 0;
}

main 开始执行
A 构造函数被调用
B 构造函数被调用
C 构造函数被调用
A::showA()
B::showB()
C::showC()
main 结束执行
C 析构函数被调用
B 析构函数被调用
A 析构函数被调用

4.菱形继承

     A/ \B   C\ /D

在这种结构下,B 和 C 都继承自 A,而 D 又同时继承自 B 和 C,这就可能会导致:

  • D 中包含两份 A 的拷贝(冗余)
  • 对 A 成员访问时出现二义性(ambiguous)

未使用虚继承的代码示例(会出问题)

#include <iostream>
using namespace std;class A {
public:int val;A() : val(10) {cout << "A 构造函数" << endl;}
};class B : public A {
public:B() {cout << "B 构造函数" << endl;}
};class C : public A {
public:C() {cout << "C 构造函数" << endl;}
};// D 从 B 和 C 派生
class D : public B, public C {
public:D() {cout << "D 构造函数" << endl;}
};int main() {D obj;// obj.val = 100; // ❌ 错误!val 在 B::A 和 C::A 中各有一份,二义性obj.B::val = 100;  // ✅ 指定路径obj.C::val = 200;cout << "B::val = " << obj.B::val << endl;cout << "C::val = " << obj.C::val << endl;return 0;
}

A 构造函数
B 构造函数
A 构造函数
C 构造函数
D 构造函数
B::val = 100
C::val = 200

A 的构造函数被调用了两次 → 产生了两个 val

5.虚继承

使用虚继承解决菱形继承问题

class B : virtual public A { ... };
class C : virtual public A { ... };

A 构造函数
B 构造函数
C 构造函数
D 构造函数
val = 999

类型是否重复构造 A是否有二义性成员数量
普通继承✅ 是❌ 有两份 A 成员
虚继承❌ 否✅ 无一份 A 成员

七、抽象类与接口

知识点描述
抽象类含有纯虚函数的类
纯虚函数virtual void func() = 0;
接口(模拟)纯虚函数 + 无成员变量的抽象类

抽象类是包含至少一个纯虚函数(= 0)的类,不能被实例化,仅用于被继承,定义接口规范。

class Animal {
public:virtual void sound() = 0;  // 纯虚函数
};

八、this指针、对象指针 和 const对象

this:指针指向当前对象本身,用于区分成员与形参重名

const成员函数:承诺不修改对象状态,可被 const 对象调用
对象指针(Object Pointer):是指指向类对象的指针变量。你可以把它理解为“类的指针版变量”,它可以用来访问类的成员、动态创建对象、实现多态等。

class A {int x;
public:void set(int x) { this->x = x; }int get() const { return x; }
};class Person {
public:void sayHello() {cout << "Hello!" << endl;}
};int main() {Person p;Person* ptr = &p;   // 对象指针,指向 p//ptr 是一个指针变量,类型是 Person*,指向对象 p。通过 -> 操作符访问类的成员。ptr->sayHello();    // 通过指针访问成员函数return 0;
}

动态创建对象 + 对象指针

class Person {
public:string name;void say() {cout << "I'm " << name << endl;}
};
int main() {Person* p = new Person();  // 在堆上创建对象p->name = "Tom";p->say();                  // 输出:I'm Tomdelete p;                  // 手动释放内存return 0;
}

配合多态使用:

class Animal {
public:virtual void speak() {cout << "Animal speaks" << endl;}
};
class Cat : public Animal {
public:void speak() override {cout << "Meow!" << endl;}
};
int main() {Animal* ptr = new Cat();   // 父类指针指向子类对象ptr->speak();              // 输出:Meow!delete ptr;return 0;
}
对比项this 指针对象指针 (Person*)
定义方式编译器隐式生成程序员显式声明
存在位置类的非静态成员函数内部程序中任何地方都可定义
指向对象当前成员函数所属的对象你手动赋值的对象地址
是否可修改指向❌ 不可更改✅ 可随意更换
是否能访问私有成员✅ 可以✅ 可以(需是类内成员或友元)
使用场景成员访问歧义、链式调用、返回自身访问对象成员、函数传参、多态
写法形式this->成员ptr->成员

this 是类内部默认的对象指针,指向“自己”;对象指针是开发者创建的指针,指向某个对象,二者形式相似,语义和使用时机完全不同。

九、友元(friend)

friend 函数/类可以访问私有成员,常用于操作符重载等外部函数访问类内部数据。

友元函数

  • 友元函数不是类的成员函数,所以没有this指针,必须通过参数传递对象。
  • 友元函数中不能直接引用对象成员的名字,只能通过形参传递进来的对象或对象指针来引用该对象的成员。
#include <iostream>
using namespace std;class Box {
private:int length;
public:Box(int l) : length(l) {}// 友元函数声明friend void printLength(const Box& b);
};// 友元函数定义(可以访问私有成员)
void printLength(const Box& b) {cout << "Length: " << b.length << endl;
}int main() {Box b(10);printLength(b);  // 输出:Length: 10return 0;
}
//printLength 不是类的成员函数,但可以访问 Box 的私有成员。

友元类
一个类 A 声明另一个类 B 为它的友元类,意味着 类 B 可以访问 A 的私有成员和保护成员。

class B;  // 先声明类 B,避免未定义class A {
private:int secret = 42;// B 是 A 的友元类friend class B;
};class B {
public:void showASecret(A& a) {std::cout << "A 的秘密是:" << a.secret << std::endl;}
};
//A 的秘密是:42     //类 B 通过声明为 A 的友元类,获得访问 A 的私有变量 secret 的权限。

注意事项:

特点说明
单向性A 声明 B 是友元类,B 能访问 A,但 A 不能访问 B,除非 B 也声明 A 是友元类
编译器视为特殊关系不是继承、不是组合关系,是编译器信任授予
可访问私有和保护成员仅访问权限,不继承特性,不改变成员属性
通常用于操作类之间需要深入访问的情况,如调试器类、控制器类、实现细节类等

友元类VS友元函数

比较项友元类友元函数
授权对象整个类单个函数
可访问内容所有成员(包括私有/保护)同上
适合场景紧耦合类之间单独的辅助函数

友元类是类与类之间互信的一种机制,用于实现更紧密的访问控制,但应谨慎使用,避免破坏封装性和高耦合。

十、运算符重载(Operator Overloading)

C++ 允许用户重载运算符,使自定义类型支持类似内置类型的操作语义,从而提升可读性和表达力。

#include <iostream>
using namespace std;class Complex {
public:double real, imag;Complex(double r, double i) : real(r), imag(i) {}// 重载 + 运算符Complex operator+(const Complex& other) {return Complex(real + other.real, imag + other.imag);}void display() {cout << real << " + " << imag << "i" << endl;}
};int main() {Complex a(1.2, 3.4);Complex b(5.6, 7.8);Complex c = a + b;  // 使用重载的 +c.display();        // 输出:6.8 + 11.2ireturn 0;
}

通过重载 +,使自定义类型像内置类型一样直观相加。

十一、类的静态成员 static

静态成员变量属于类本身而非某个对象,所有对象共享同一份数据。静态成员函数不能访问非静态成员,只能访问类中静态数据成员。

class Counter {static int count;
public:static int getCount();
};
------------------静态数据成员------------------
//类内声明,类外定义-
class xxx
{static 数据类型 静态数据成员名;
}
数据类型 类名::静态数据成员名=初值
//访问
类名::静态数据成员名;
对象名.静态数据成员名;
对象指针名->静态数据成员名;----------------静态成员函数----------------------
//类内声明,类外定义
class xxx
{static 返回值类型 静态成员函数名(参数列表);
}
返回值类型 类名::静态成员函数名(参数列表)
{函数体;
}
//访问
类名::静态成员函数名(参数列表);
对象名.静态成员函数名(参数列表);
对象指针名->静态成员函数名(参数列表);

十二、类和结构体区别

C 中只有结构体(struct)用于聚合数据;
C++ 中 class 和 struct 功能几乎相同,主要区别是默认访问权限不同,开发中通常 class 用于复杂对象,struct 用于简单数据结构。

http://www.dtcms.com/a/292214.html

相关文章:

  • vscode gdb调试c语言过程
  • IDEA-自动格式化代码
  • IDEA全局Maven配置
  • 【IDEA】如何在IDEA中通过git创建项目?
  • 【C++】nlohmann/json
  • 哔哩哔哩视觉算法面试30问全景精解
  • Kafka单条消息长度限制详解及Java实战指南
  • 新品如何通过广告投放精准获取流量实现快速增长
  • 【RAG优化】PDF复杂表格解析问题分析
  • 北宋政治模拟(deepseek)
  • 力扣面试150题--寻找峰值
  • 如何为每个参数案例自动执行当前数据集
  • 双指针算法介绍及使用(上)
  • rk3568平台记录一次推流卡顿分析过程
  • Next.js项目目录结构详解:从入门到精通的最佳实践指南
  • 一文详解策略梯度算法(REINFORCE)—强化学习(8)
  • 新手向:基于Python的剪贴板历史增强工具
  • Jiasou TideFlow AIGC SEO Agent:全自动外链构建技术重构智能营销新标准
  • 数据库 × 缓存双写策略深度剖析:一致性如何保障?
  • Apache Ignite缓存基本操作
  • Redis原理之缓存
  • uni-calendar自定义签到打卡颜色
  • Java-79 深入浅出 RPC Dubbo Dubbo 动态路由架构详解:从规则设计到上线系统集成
  • .NET 8.0 中有哪些新的变化?
  • 数据结构自学Day12-- 排序算法2
  • 前端面试专栏-工程化:29.微前端架构设计与实践
  • Vue 3 面试题全套题库
  • Vue项目中的AJAX请求与跨域问题解析
  • paddleocr微调训练学习笔记
  • 符号绑定详解:ES6模块中的“诡异”现象与内存机制