C++入门
文章目录
命令空间
命明空间的两种使用方式及自定义命名空间
1.1using指令申明
例如
using namespace std;
将整个命名空间引入当前作用域,后续可直接使用cout等名称
1.2作用域限定符(::)直接访问
-
概念:
::, 用于指定一个成员(如函数或变量)属于特定的类或命名空间,例如,在类的外部定义成员函数时,::用于指定该函数属于那个类 -
此处是用于指定属于哪个命名空间
std::cout << "Hello World!" << std::endl;
- 此处是用于指定属于那个类
void host::info(string newName,string newNum) //指定这个函数属于host类
{
name=newName;
num=newNum;
}
2.自定义命名空间
例如
namespace add{
int add(int a,int b){
return a+b;
}
}
输入输出
尖括号指向谁,表示数据要往哪里去
1.标准输出流(cout)
类似于C语言的printf
#include <iostream>
using namespace std;
int main()
{
int a=1;
int b=2;
cout << "Hello World!" << endl;
cout <<a<<"+"<<b<<"="<<a+b<<endl;
return 0;
}
cout << “Hello World!” << endl;
相当于c语言中的printf(“Hello World!\n”);
cout <<a<<“+”<<b<<“=”<<a+b<<endl; c++中的输出拼接
相当于C语言中的printf(“%d+%d=%d\n”,a,b,a+b);
运行结果
2.标准输入流(cin)
类似于C语言中的scanf
#include <iostream>
using namespace std;
int main()
{
int a=0;
int b=0;
cin>>a;
cin>>b;
cout << "Hello World!" << endl;
cout <<a<<"+"<<b<<"="<<a+b<<endl;
return 0;
}
运行结果
宽字符
1.概述
wchar_t 是宽字符类型,每个字符占 2 或 4 个字节(取决于平台,通常 Windows 上是 2 字节 wchar_t,Linux 上是 4 字节)。
2.用法示例
#include <iostream>
#include <wchar.h>
using namespace std;
int main()
{
wchar_t hello[]=L"Hello World!";
wcout << hello << endl;
return 0;
}
注解
在 C/C++ 中,L 是一个字符串字面量的前缀,用来表示宽字符字符串
没有 L:"Hello" 是普通窄字符串,每个字符占 1 个字节(char 类型)。
加上 L:L"Hello" 是宽字符串,每个字符占 2 或 4 个字节(取决于平台,通常 Windows 上是 2 字节 wchar_t,Linux 上是 4 字节)。
配合wcout使用
窄字符(char / string)→ 用 std::cout
宽字符(wchar_t / wstring)→ 用 std::wcout)
运行结果
内联函数
1.概述
内联函数是 C++ 中的一种优化机制,通过 inline 关键字建议编译器在调用处直接展开函数代码,以减少函数调用的开销(压栈、跳转、返回等)
2.特点
- 语法
inline int add(int a, int b)
{
return a+b;
}
-
作用
消除函数调用的开销(参数传递、压栈、跳转、返回等),对于短小、频繁调用的函数效果明显。对比项 普通函数 内联函数 执行过程 跳转到函数体 → 执行 → 跳回 直接执行复制出来的代码 -
本质:建议而非命令
inline 只是向编译器提建议,编译器决策,即使函数被声明为内联函数,编译器即也可决定不执行内联(特别是函数体过大、递归函数等)。现代编译器会根据优化级别自动内联合适的函数,即便没有 inline 关键字。 -
链接规则
内联函数的定义(而非仅仅是声明)必须在每个调用它的源文件中都可见(通常定义在头文件中)。
4.注意事项
过度使用
过度使用内联会导致可执行文件体积增大,反而降低缓存命中率
编译器决定
最终是否使用函数内联由编译器决定,即使函数被声明为内联函数
什么时候使用
适合:函数体极短、调用极其频繁
不适合:函数体复杂、递归函数等
5.用法示例
#include <iostream>
using namespace std;
inline int add(int a, int b)
{
return a+b;
}
int main()
{
int c=add(2,4);//编译器可能会将此优化为 int c =2+4;
cout <<c << endl;
return 0;
}
运行结果
Lambda
1.概述
它是 C++11 引入的一种匿名函数对象的写法,非常适合用来写短小、临时的可调用代码
2.语法
[capture clause](parameters) -> return_type { body }
- 捕获列表[capture clause]:用于捕获外部变量,在Lambda表达试中可以访问这些变量
- 参数列表(parameters) :与普通函数参数列表类似
- 返回类型(return_type):auto 推导出 lambda 的匿名函数对象类型,指定返回类型->return_type,如果函数只有一条返回语句,可以省略返回类型
- 函数体(body):Lambda表达式的函数体,包含要执行的代码
3.捕获方式
1.[ ]
不捕获任何外部变量
#include <iostream>
using namespace std;
int main()
{
int x=1;
int y=1;
auto a=[](int x, int y)->int{
return x+y;
};
int b=a(x,y);
cout <<b<< endl;
return 0;
}
运行结果
2.[ x,y]
捕获外部变量x、y,不能修改变量,类似只读
#include <iostream>
using namespace std;
int main()
{
int x=1;
int y=2;
auto a=[x,y]()->int{
return x+y;
};
int b=a();
cout <<b<< endl;
return 0;
}
运行结果
3.[=]
捕获全部外部变量,不能修改变量,类似只读
#include <iostream>
using namespace std;
int main()
{
int x=5;
int y=2;
auto a=[=]()->int{
return x+y;
};
int b=a();
cout <<b<< endl;
return 0;
}
运行结果
4.[&]
引用的方式,捕获所有变量,可以修改变量,类似指针,可读可写
#include <iostream>
using namespace std;
int main()
{
int x=5;
int y=2;
auto a=[&]()->int{
x=9; //引用捕获方式,可以修改变量
return x+y;
};
int b=a();
cout <<b<< endl;
return 0;
}
运行结果
4.用法示例
常见用法
#include <iostream>
using namespace std;
int getmin(int a ,int b,bool (*p)(int a,int b))
{
if(p(a,b))
{
return b;
}
else
{
return a;
}
}
int main()
{
int x=1;
int y=2; //仅当无捕获时,可隐式转换为同名函数指针
int min=getmin(x,y,[](int a,int b)->bool{
return a>b;
});
cout <<min<< endl;
return 0;
}
运行结果
类
1.概述
类的概念是面向对象编程的核心之一,其主要目的是将数据和与数据相关的操
作封装在一起。
C++类的基本结构通常包含:
1.数据成员(Attributes):定义类的属性。这些是类内部的变量,用于存储对象的状态。
2.成员函数(Methods):定义类的行为。这些是可以操作对象的数据成员的函数。
3.构造函数和析构函数:特殊的成员函数。构造函数在创建对象时自动调用,用于初始化对象。析构函数在对象销毁时调用,用于执行清理操作。
4.访问修饰符:如public,private,protected,用于控制对类成员的访问权限。例如,public
成员可以在类的外部访问,而private成员仅类内部(及友元)可访问,默认权限。protected:类内部和派生类可访问。
5.继承:允许一个类继承另一个类的特性。这是代码重用和多态性的关键。
2.访问权限
| 访问权限 | 类内部 | 同一个类的对象(类的实例) | 派生类(子类) | 类外部 |
|---|---|---|---|---|
| public | ✔可访问 | ✔可访问 | ✔可访问 | ✔可访问 |
| private | ✔可访问 | ❌不可访问 | ❌不可访问 | ❌不可访问 |
| protected | ✔可访问 | ❌不可访问 | ✔可访问 | ❌不可访问 |
3.代码示例
一般类的定义在头文件实现,具体成员函数在源文件实现
#include <iostream>
#include <string>
using namespace std;
class host{
string name;
string num;
public:
void info(string newName,string newNum);
void prinfo();
};
class dog {
string name;
string sounds;
host* host1; //类的组合,狗有主人,这个主人可以是一个类
public: //公有成员函数作为接口,用于外部设置或获取私有成员变量
void info(string newName,string newSounds,host* newHost); //成员函数声明
void prinfo();
};
void host::info(string newName,string newNum) //成员函数,类外部实现
{
name=newName;
num=newNum;
}
void host::prinfo()
{
cout <<"主人的名字:"<<name<<endl;
cout<<"主人的电话:"<<num<<endl;
}
void dog::info(string newName,string newSounds,host* newHost)
{
name=newName;
sounds=newSounds;
host1=newHost;
}
void dog::prinfo()
{
cout <<"狗的名字:"<<name<<endl;
cout<<"狗的叫声:"<<sounds<<endl;
host1->prinfo();
}
int main()
{
dog huahua;
host* maomao= new host; //动态创建host对象, 创建(分配+构造)
maomao->info("猫猫","123456");
huahua.info("花花","汪汪",maomao);
huahua.prinfo();
delete maomao; // 释放(析构+回收内存)
return 0;
}
运行结果
引用
1.概述
引用:是已存在变量的别名。就像一个人的“绰号”和“本名”指向同一个人。
2.引用和指针的三个主要区别
| 特性 | 引用(&) | 指针(*) |
|---|---|---|
| 初始化 | 必须在定义时初始化,且绑定到一个对象 | 可以在任何时间被初始化,定义时可以不初始化,之后赋值 |
| 重新绑定 | 不可改变绑定的对象,始终指向初始对象 | 可以改变指向,重新赋值指向另一个对象 |
| 是否可为空 | 不能为空,必须引用一个有效对象 | 可以为 NULL |
3.代码示例
1.将引用作为参数
#include <iostream>
#include <string>
using namespace std;
void swap(int& ra,int& rb)
{
int temp=ra;
ra=rb;
rb=temp;
}
int main()
{
int a=5;
int b=6;
cout<<"交换前"<<endl;
cout<<"a:"<<a<<endl<<"b:"<<b<<endl;
swap(a,b);
cout<<"交换后"<<endl;
cout<<"a:"<<a<<endl<<"b:"<<b<<endl;
return 0;
}
运行结果

2.将引用作为返回值
当返回一个引用时,要注意被引用的对象的生命周期,被引用的对象必须在函数返回后依然存在。 例如:局部变量:函数结束即销毁 → 不能返回其引用
#include <iostream>
#include <string>
using namespace std;
int val[3]={1,2,3};
int& setval(int i)
{
int& ret=val[i];
return ret;
}
int main()
{
setval(1)=66;
cout<<"val[1]:"<<val[1]<<endl;
return 0;
}
运行结果
重载
1.概述
定义:在同一作用域内,可以定义多个同名但参数列表不同的函数或运算符
2.函数的重载
- 参数个数、类型或顺序不同
- 返回值类型不能作为区分依据
代码示例
#include <iostream>
#include <string>
using namespace std;
void print(int x) { cout << "int: " << x<<endl; }
void print(double x) { cout << "double: " << x<<endl; }
void print(string s) { cout << "string: " << s<<endl; }
int main()
{
print(6);
print(9.8);
print("hello");
return 0;
}
运行结果
3.运算符重载
1.概述
- 为已有运算符(如 +, -, [])赋予自定义类型的新含义
- operator 是一个关键字,后面紧跟一个运算符符号(如 +、-、=、[] 等),例如:组合起来 operator+ 就代表“加法运算符函数”。
2.代码示例
#include <iostream>
#include <string>
using namespace std;
class prit
{
public:
int x,y;
prit operator+(prit temp);
};
prit prit::operator+(prit temp)
{
prit ret;
ret.x=x+temp.x;
ret.y=y+temp.y;
return ret;
}
int main()
{
prit p1;
p1.x=1;
p1.y=2;
prit p2;
p2.x =1;
p2.y=2;
prit p3=p1+p2; //p1被作为调用对象,p2被作为参数传入,等价于 p1.operator+(p2)
cout <<"p3.x:"<<p3.x<<endl;
cout<<"p3.y:"<<p3.y<<endl;
return 0;
}
运行结果
补充说明
这里参数temp是通过值传递,也可以用const prit,这样避免了拷贝构造的开销,又保证了加法运算符不会无意改变右操作数的值
prit operator+(const prit& other) const;,后面这个const,可以确保某些函数不会意外的改变对象的状态,还可以使得这个函数可以在在常量对象被调用
为什么可以使得常量对象被调用?
- 在 C++ 中,常量对象(const 对象)只能调用被声明为 const 的成员函数。
为什么常量对象只能调用const成员函数?
- 常量对象意味着它的成员变量不能被修改。
- 如果调用一个非 const 成员函数,编译器无法保证该函数不会修改成员变量(即使它实际上没有修改,编译器也会拒绝),因此会直接报错,防止潜在的修改。而 const 成员函数承诺不会修改对象状态,编译器允许常量对象调用它。
4.注意事项
- 一致性:重载后的运算符行为应与内置类型或常规语义一致。例如,
+运算符通常应该实现加法,而不是其他意外的操作 - 复杂性:过度使用会让代码难以理解和维护
构造函数
1.概述
构造函数是类的一种特殊成员函数,在创建对象时自动调用执行,构造成员变量的初始化值、内存空间
构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回void。构造函数可用于为某些成员变量设置初始值。
2无参构造函数
默认为无参构造函数,如果用户没有定义任何构造函数,编译器会自动生成一个空的无参构造函数(但不会初始化内置类型成员)。
代码示例
#include <iostream>
#include <string>
using namespace std;
class dog
{
public:
string name;
dog(){
cout<<"无参构造函数被调用"<<endl;
}
};
int main()
{
dog xiaohuang;
return 0;
}
运行结果
3.带参数构造函数
代码示例
#include <iostream>
#include <string>
using namespace std;
class dog
{
public:
string name;
dog(string name1){ //带参数构造函数
name=name1;
cout<<name<<endl;
}
};
int main()
{
dog hua("小花");
return 0;
}
运行结果
4.参数列表的方式构造
1.概述
参数列表的方式构造指的是 C++ 构造函数中的初始化列表语法,用于在进入构造函数体之前初始化成员变量。
基本语法class 类名 { public: 类名(参数列表) : 成员1(值1), 成员2(值2), ... { // 构造函数体 } };
为什么用初始化列表?
| 原因 | 说明 |
|---|---|
| 效率 | 直接初始化成员,避免先默认构造再赋值 |
| 必要性 | 有些成员必须在初始化列表中初始化:const 成员、引用成员、没有默认构造函数的类成员、基类带参数的构造函数 |
| 顺序确定性 | 成员按声明顺序初始化,而不是按列表顺序 |
2.代码示例
#include <iostream>
#include <string>
using namespace std;
class dog
{
public:
string name;
dog(string name1):name(name1){
cout<<name<<endl;
}
};
int main()
{
dog huang("小黄");
return 0;
}
运行结果
this关键字
1.概述
在C++中,this 关键字是一个指向调用对象的指针。它在成员函数内部使用,用于引用调用该函数的对象。使用 this 可以明确指出成员函数正在操作的是哪个
2.用法示例
#include <iostream>
#include <string>
using namespace std;
class dog
{
public:
string name;
dog(string name){
this->name=name; //使用this指针区分成员变量和参数
}
void print();
dog& setname(string name);
};
void dog::print()
{
cout<<this->name<<endl; //也可以不使用this->,直接写name
}
dog& dog::setname(string name)
{
this->name=name; //更新名字
return *this; //返回调用对象的引用
}
int main()
{
dog huang("小黄");
huang.print();
//链式调用
huang.setname("大黄").print(); //huang.setname("大黄"),返回调用对象的引用,即huang
return 0;
}
运行结果
new关键字
1.概述
在 C++ 中,new 关键字用于动态分配内存。它是 C++ 处理动态内存分配的主要工具之一,允许在程序运行时根据需要分配内存
2.基本用法
1.分配单个对象
int* ptr = new int; // 类似C语言中:int *p = (int *)malloc(sizeof(int));
2.分配对象数组
int* arr = new int[10]; //类似C语言中:int *arr = (int *)malloc(sizeof(int)*10);
3.创建类对象,对于单个对象,可以使用构造函数的参数
host* maomao= new host;
MyClass* obj = new MyClass(arg1, arg2);
4.与 delete 配对使用
- 释放单个对象
delete ptr; // 释放 ptr 指向的对象
- 释放对象数组
delete[] arr; // 释放 arr 指向的数组
- 释放类
delete maomao; // 释放(析构+回收内存)
析构函数
1.概述
析构函数是C++中的一个特殊的成员函数,它在对象生命周期结束时被自动调用,用于执行对象销毁前的清理工作。析构函数特别重要,尤其是在涉及动态分配的资源(如内存、文件句柄、网络连接等)的情况下。
2.基本特性
1.名称:析构函数的名称由波浪号(~) 后跟类名构成,如~Myc1ass()。
2.无返回值和参数:析构函数不接受任何参数,也不返回任何值。
3.自动调用:当对象的生命周期结束时(例如,一个局部对象的作用域结束,或者使用delete删除一个动态分配的对象),析构函数会被自动调用。
4.不可重载:每个类只能有一个析构函数。
5.继承和多态:如果一个类是多态基类,其析构函数应该是虚的。
3.代码示例
#include <iostream>
#include <string>
using namespace std;
class dog
{
private:
int* datas;
public:
string name;
dog(string name,int size){
this->name=name;
this->datas=new int[size];
}
~dog(){
cout<<"解析函数被调用"<<endl;
delete[] this->datas;
}
};
int main()
{
dog* hua=new dog("花",5);
delete hua;//delete hua 会调用 dog 类的析构函数
return 0;
}
补充说明
当用户没有定义析构函数时,编译器会生成一个默认析构函数
默认析构函数会
自动调用非静态成员对象(如 string name)的析构函数(正确释放 name 的内部资源)。
但不会释放普通指针成员(如 int* datas)所指向的内存。它只销毁指针变量本身(指针占用的栈内存),而指针指向的堆内存仍然存在,导致内存泄漏
运行结果
静态成员
1.概述
在 C++ 中,静态成员是归属于类本身而非某个具体对象的成员。无论创建多少个对象,静态成员在内存中只有一份副本,被所有对象共享。
2.静态成员变量
- 在类内声明时使用 static 关键字。
- 必须在类外单独定义并初始化
#include <iostream>
using namespace std;
class MyClass {
public:
static int count; // 声明静态成员变量
};
// 类外定义(不重复 static)
int MyClass::count = 0;
int main()
{
MyClass obj;
cout<<obj.count<<endl;
cout<<MyClass::count<<endl;
return 0;
}
访问方式
- 通过类名:MyClass::count
- 通过对象:obj.count
特点
- 存储于静态存储区,生命周期贯穿整个程序。
- 不占用对象内存,即 sizeof(MyClass) 不包含静态成员。
- 访问控制(public/protected/private)同样生效
3.静态成员函数
- 使用 static 关键字声明。
- 只能访问静态成员(变量或其他函数),不能直接访问非静态成员
#include <iostream>
using namespace std;
class MyClass {
public:
static int data;
static void show() {
cout << data<<endl; // ✅ 可以访问静态成员
}
};
int MyClass::data=1;
int main()
{
MyClass obj;
obj.show();
MyClass::show();
return 0;
}
调用方式: MyClass::show() 或 obj.show()
常见用途
管理类级别的共享数据(如对象计数)
示例:统计对象数量
#include <iostream>
using namespace std;
class Object {
private:
static int count;
public:
Object() { count++; }
~Object() { count--; }
static int getCount() { return count; }
};
int Object::count = 0; // 初始化
int main() {
Object a, b;
cout << Object::getCount()<<endl; // 输出 2
{
Object c;
cout << Object::getCount()<<endl; // 输出 3
}
cout << Object::getCount()<<endl; // 输出 2
return 0;
}
继承
1.概述
继承是面向对象编程的核心机制,允许一个类(派生类,也称子类)基于另一个类(基类,也称父类)来构建,从而复用和扩展已有功能。
2.特点
1.代码重用:子类继承了父类的属性和方法,减少了代码的重复编写。
2.扩展性:子类可以扩展父类的功能,添加新的属性和方法,或者重写(覆盖)现有的方法。
3.多态性:通过继承和虚函数,C++支持多态,允许在运行时决定调用哪个函数。
3.基本用法
在C++中,继承可以是公有(public)、保护(protected)或私有(private)的,这决定了基类成员在派生类中的访问权限。
:表示继承
class Base {
// ...
};
class Derived : public Base { // 继承方式:public/protected/private
// ...
};
4.权限对继承的影响
| 继承方式 | 基类 public 成员 | 基类 protected 成员 | 基类 private 成员 |
|---|---|---|---|
| public | 派生类中为 public | 派生类中为 protected | 不可访问 |
| protected | 派生类中为 protected | 派生类中为 protected | 不可访问 |
| private | 派生类中为 private | 派生类中为 private | 不可访问 |
| 访问权限 | 类内部 | 同一个类的对象(类的实例) | 派生类(子类) | 类外部 |
|---|---|---|---|---|
| public | ✔可访问 | ✔可访问 | ✔可访问 | ✔可访问 |
| private | ✔可访问 | ❌不可访问 | ❌不可访问 | ❌不可访问 |
| protected | ✔可访问 | ❌不可访问 | ✔可访问 | ❌不可访问 |
派生类构造函数
1.概述
在C++中,派生类可以通过其构造函数的初始化列表来调用基类的构造函数。这是在构造派生类对象时初始化基类部分的标准做法。当创建派生类的对象时,基类的构造函数总是在派生类的构造函数之前被调用
2.示例
1.基类完全没有写任何构造函数
编译器会自动生成一个隐式的默认构造函数,派生类可以正常调用它,不需要在初始化列表中显式写出。
#include <iostream>
using namespace std;
class Base {
// 完全没有构造函数的声明
int x;
// 编译器会生成 Base()
};
class Derived : public Base {
public:
Derived() {
cout << "Derived constructed" << endl;
}
};
int main() {
Derived d; // 先调用编译器生成的 Base()
return 0;
}
基类构造没有打印,但确实被调用了

2.如果没有明确指定,将调用基类的默认构造函数。
#include <iostream>
using namespace std;
class Base {
public:
Base() { cout << "Base default constructor" << endl; }
};
class Derived : public Base {
public:
Derived() { cout << "Derived constructor" << endl; }
// 初始化列表中没有写 Base(),编译器会自动调用 Base()
};
int main() {
Derived d;
return 0;
}

3.如果基类没有默认构造函数,或者你需要调用一个特定的基类构造函数,就需要在派生类构造函数的初始化列表中明确指定。
基类没有默认的构造函数
#include <iostream>
using namespace std;
class Base {
public:
Base(int x) : value(x) { cout << "Base(int) called, value=" << value << endl; }
// 注意:没有定义 Base(),所以没有默认构造函数
private:
int value;
};
class Derived : public Base {
public:
// 错误写法:Derived() {}
// 编译错误:'Base' 没有默认构造函数
// 正确写法:显式调用 Base(int)
Derived(int a) : Base(a) { cout << "Derived(int) called" << endl; }
};
int main() {
Derived d(42);
return 0;
}

调用特定的基类构造函数
#include <iostream>
using namespace std;
class Base {
public:
Base() { cout << "Base default" << endl; }
Base(string msg) : message(msg) { cout << "Base(string): " << message << endl; }
private:
string message;
};
class Derived : public Base {
public:
// 可以选择调用默认构造
Derived() : Base() { cout << "Derived default" << endl; }
// 或者调用带参构造
Derived(string s) : Base(s) { cout << "Derived(string)" << endl; }
};
int main() {
Derived d1; // 调用 Base()
Derived d2("hello"); // 调用 Base(string)
return 0;
}

虚函数
1.概述
虚函数是 C++ 实现运行时多态的核心机制,它允许通过基类指针或引用调用派生类中重写的函数版本,在C++中,virtual和override关键字用于支持多态,尤其是在涉及类继承和方法重写的情况
2.virtual关键字
作用
在基类中声明一个成员函数为虚函数,表示该函数可以被派生类重(override)
特点
- 只在基类中需要写 virtual;派生类中重写该函数时可以不写(但推荐写 override)。
- 虚函数可以被继承:一旦基类函数是 virtual,所有派生类的同名同参函数自动成为虚函数。
- 构造函数不能是 virtual;如果类中有虚函数,
析构函数应该是声明为virtual - 静态成员函数不能是 virtual(虚函数依赖具体的对象/实例,而静态函数不需要,静态函数属于这个类)
在C++中,成员函数默认不是虚函数。只有显式地使用virtua1关键字才会成为虚函数。
3.override关键字
作用
显式地告诉编译器“这个函数意在重写基类的一个虚函数”。如果实际上并没有成功重写(例如函数名不匹配、基类没有对应的虚函数等),编译器会报错。
4.示例
#include <iostream>
using namespace std;
class Base {
public:
virtual void show() { cout << "Base::show()" << endl; }
};
class Derived : public Base {
public:
void show() override { cout << "Derived::show()" << endl; } // 重写
};
int main() {
Derived d;
d.show();
return 0;
}
虚函数被重写,所以输出不是Base::show()

多重继承
1.概述
在C++中,多重继承是一种允许一个类同时继承多个基类的特性。这意味着派生类可以继承多个基类的属性和方法。多重继承增加了语言的灵活性,但同时也引入了额外的复杂性,特别是当多个基类具有相同的成员时。
在多重继承中,派生类继承了所有基类的特性。这包括成员变量和成员函数。如果不同的基类有相同名称的成员,则必须明确指出所引用的是哪个基类的成员
2.示例
#include <iostream>
using namespace std;
class Base {
public:
void show() { cout << "Base::show()" << endl; }
};
class Derived {
public:
void show() { cout << "Derived::show()" << endl; }
};
class c:Base,Derived //同时继承 Base和Derived
{
public:
void display()
{
Derived::show(); //Base和 Derived都有show,所以需指明
}
};
int main() {
Derived d;
d.show();
return 0;
}

菱形继承和虚继承
1.菱形继承
1.概述
两个派生类继承自同一个基类,而另一个类又同时继承这两个派生类,形成的继承结构形如菱形
示例图
Grand
/ \
Base1 Base2
\ /
Derived
2.问题
在菱形继承结构中,一个类继承自两个具有共同基类的类时,会导致共同基类的成员在派生类中存在两份拷贝 (Derived继承于Base1和Base2,Base1和Base2又继承于Grand,Grand会被拷贝两份) ,这不仅会导致资源浪费,还可能引起数据不一致的问题。
导致
- 内存冗余
- 访问二义性(不知道该用哪一份)
2.虚继承
1.概述
虚继承是C++中一种特殊的继承方式,主要用来解决多重继承中的菱形继承问题。虚继承通过确保共同基类的单一实例存在于继承层次中,来解决菱形继承存在的问题,在继承时使用 virtual 关键字,告诉编译器:该基类在派生类中只保留一份共享实例
3.示例
#include <iostream>
using namespace std;
class Grand{
public:
int data;
Grand(int data)
{
this->data=data;
}
void print()
{
cout<<data<<endl;
}
};
class Base1: virtual public Grand { //虚继承 继承Grand
public:
Base1(int data):Grand(data) {
}
};
class Base2:virtual public Grand { //虚继承 继承Grand
public:
Base2(int data):Grand(data) {
}
};
class c:public Base1,public Base2 //同时继承 Base1和Base2
{
public:
c(int data):Grand(data),Base1(data),Base2(data) //构造继承要写全,同时要注意顺序
{
}
};
int main()
{
c maomao(8);
maomao.print();
return 0;
}

多态
1.概述
多态是面向对象编程的核心概念之一,指同一接口或符号,在不同类型对象上表现出不同行为。
一个生活类比
假设你有一个遥控器(统一的操作接口),上面有一个“启动”按钮。
你拿这个遥控器去控制空调:按“启动” → 空调开始制冷。
你拿同一个遥控器去控制电视:按“启动” → 电视屏幕亮起。
同样都是按“启动”按钮,结果却不一样。这就是多态:同一个指令,在不同设备上产生不同的效果。
2.优点
多态让代码更简短、灵活、易扩展
1. 代码复用,消除重复
不需要为每个派生类单独编写相同的调用逻辑。
例如:一个 render(Shape&) 函数就能处理所有 Circle、Rectangle、Triangle,不用为每种形状重写一遍渲染函数。
2. 高可扩展性
新增派生类(如 Ellipse)时,原有代码无需修改,只要新类正确重写虚函数即可。这降低了引入新功能的成本和风险。
3. 统一接口,降低耦合
调用方只依赖基类的抽象接口,而不依赖具体类型。
例如:render(Shape& s) 不关心 s 到底是什么派生类,,它统一处理所有 Shape 家族
4. 支持面向接口编程
通过纯虚函数定义接口(抽象类),实现“做什么”与“怎么做”分离。
便于团队协作、模块替换、单元测试
3.如何实现多态
通过继承 + 虚函数 + 基类指针/引用实现
1.使用虚函数(Virtual Function):
使用关键字virtual,在基类中定义一个虚函数,这个函数可以在任何派生类中被“重写”
2.创建派生类并重写虚函数:
在派生类中,我们提供该虚函数的具体实现。
3.通过基类的引用或指针调用虚函数:
当我们使用基类类型的指针或引用来调用虚函数时,实际调用的是对象的实际类型(派生类)中的函数版本。
4.示例
#include <iostream>
using namespace std;
class Shape {
public:
virtual void draw() { cout << "绘制图形\n"; }
};
class Circle : public Shape {
public:
void draw() override { cout << "绘制圆形\n"; }
};
class Rectangle : public Shape {
public:
void draw() override { cout << "绘制矩形\n"; }
};
// 多态函数:通过基类引用调用
void render(Shape& s) {
s.draw(); // 运行时决定调用哪个派生类的 draw()
}
int main() {
Circle c;
Shape* r=new Rectangle; //通过基类指针调用
render(c); // 输出:绘制圆形
r->draw(); // 输出:绘制矩形
return 0;
}

抽象类
1.概述
抽象类是至少包含一个纯虚函数的类。它不能实例化对象,只能作为基类被继承,用于定义统一的接口规范。
纯虚函数
virtual void func() = 0; // 纯虚函数,没有函数体
2.特点
- 不能创建对象(不能实例化)
- 可以定义指针或引用(用于多态)
派生类必须实现所有纯虚函数,否则派生类也是抽象类- 可以包含普通成员函数、成员变量、构造函数(供派生类调用)
3.示例
#include <iostream>
using namespace std;
class Shape {
public:
virtual void draw()=0; //纯虚函数
virtual void comdraw()=0;
};
class Circle : public Shape {
public: //必须实现所有纯虚函数,否则是抽象类
void draw() override { cout << "绘制圆形中\n"; }
void comdraw() override { cout << "绘制圆形完成\n"; }
};
class Rectangle : public Shape {
public: //必须实现所有纯虚函数,否则是抽象类
void draw() override { cout << "绘制矩形中\n"; }
void comdraw() override { cout << "绘制矩形完成\n"; }
};
// 多态函数:通过基类引用调用
void render(Shape& s) {
s.draw(); // 运行时决定调用哪个派生类的 draw()
s.comdraw();
}
int main() {
Circle c;
render(c);
Shape* r=new Rectangle; //通过基类指针调用
r->draw();
r->comdraw();
return 0;
}

纯虚函数—接口
1.概述
接口是一个编程概念,指一组纯虚函数的集合,用于定义“能做什么”,而不提供具体实现。
C++ 中实现接口
-
1.定义抽象类
创建一个包含纯虚函数的抽象类,这些函数构成了接口的一部分。这些函数在抽象类中只有声明而没有具体的实现。 -
2.派生类实现接口
派生类继承抽象类,并实现其中的纯虚函数,以具体实现接口定义的方法
一般不用于多态,但是可以多态
2.示例
#include <iostream>
using namespace std;
class eat {
public:
virtual void eatfood()=0; //纯虚函数
};
class paiting{
public:
virtual void tool()=0; //纯虚函数
};
class person :paiting,eat{
public:
void eatfood() override{};
void tool() override{};
};
class dog :eat{
public:
void eatfood() override{};
};
int main() {
dog huang;
person maomao;
return 0;
}
更多推荐


所有评论(0)