0、前言:
- 因为之前已经有C的基础概念,那么对于C++的学习最好是对比着学,才能高效地掌握C++基础。
- C++ 是从 C 语言发展而来的,保留了 C 的核心语法和底层操作能力,同时引入了面向对象编程(Object-Oriented Programming:OOP)、泛型编程(模板)、异常处理等高级特性。
1、编程范式:
- C语言是纯过程式编程(Procedure-Oriented Programming:POP),不支持类、继承、多态等面向对象的特性。C++既支持过程式、也支持面向对象和泛型编程(模板)。两种语言的代码基本结构如下:
// C语言基本结构
#include<stdio.h>
// 其他函数声明
int main()
{
// 主函数体
return 0
}
// C++基本结构
#include<iostream>
using namespace std; // 使用命名空间std
// 其他函数声明
int main(){
// 主函数体
return 0
}
- 面向过程开发的最基本单位是函数,面向对象的开发的最基本单位是类;C 语言是面向过程编程,其 I/O 库设计为一系列全局函数,通过 stdio.h 声明这些函数;C++ 是面向对象编程的扩展,其 I/O 库基于 “流(Stream)” 的概念设计,通过类和对象实现;
2、C与C++对比的一些补充知识:
- 函数原型也叫函数声明。【c和c++共有的规则】
- 类和结构体名称首字母大写。【c和c++共有的规则】
- 类和结构体最后定义完,花括号后面加分号,函数定义完,花括号后面不用加分号。【c和c++共有的规则】
- 不论是c还是c++,它们的函数传参中都可以分为,值传递和地址传递两种方式,其中地址传参有两种方式,一种就是指针传递,一种就是引用传递【引用是c++中特有的用于替换指针的方式】,只要是地址传参,都会改变实参的值。【c和c++共有的规则】
- 函数的默认参数(C++特有):C 语言不支持函数的默认参数,而C++ 支持函数的默认参数。定义函数的时候,形参可以有默认值。调用的时候,如果有实参就调用实参,如果没有实参,就调用默认值;一般默认参数给到声明,但是不能给声明和定义中都给,都给就会出现重定义错误;
- 内联函数(理解作用):内联函数就是代码片段的快捷方式,不需要像一般函数一样跳转执行,内联函数优点就是执行速度快,没有额外的调用开销,适合简短且常用的函数,使用内联函数的代价是会增加代码体积,使用方式和正常函数一样,就是在定义的时候在函数前面加“inline”,如果函数定义和函数声明是分开的话,inline必须加在定义位置,声明位置可加可不加。【c和c++都支持内联函数】
#include<iostream>
using namespace std;
int add(int a, int b);
int main() {
cout << add(10,20);
return 0;
}
inline int add(int a, int b) // 内联函数的关键是在定义处添加inline关键字
{
return a + b;
}
- ★函数重载(C++特有):用一个函数实现多个功能(要么形参的类型不一样、要么形参的个数不一样),C语言中不支持函数重载,只有C++语言(后缀是.cpp的文件)才支持。函数重载的要求是:参数列表(参数类型、个数或顺序)不同。
#include <iostream>
using namespace std;
// 1. 两个整数相加
int add(int a, int b) {
cout << "整数相加: ";
return a + b;
}
// 2. 两个小数相加(参数类型不同)
double add(double a, double b) {
cout << "小数相加: ";
return a + b;
}
// 3. 三个整数相加(参数个数不同)
int add(int a, int b, int c) {
cout << "三个整数相加: ";
return a + b + c;
}
// 函数1:先接收整数,再接收字符串
void printInfo(int age, string name) {
cout << "姓名: " << name << ", 年龄: " << age << endl;
}
// 函数2:先接收字符串,再接收整数(参数顺序不同)
void printInfo(string name, int age) {
cout << "年龄: " << age << ", 姓名: " << name << endl;
}
int main() {
cout << add(3, 5) << endl; // 调用第一个add函数
cout << add(2.5, 3.8) << endl; // 调用第二个add函数
cout << add(10, 20, 30) << endl; // 调用第三个add函数
// 调用第一个函数(int在前,string在后)
printInfo(25, "张三");
// 调用第二个函数(string在前,int在后)
printInfo("李四", 30);
return 0;
}
- C++ 中的结构体(struct)与 C 语言中的结构体在调用方式和功能上有明显区别,主要体现在 C++ 对结构体进行了面向对象的扩展。C 语言的结构体仅能包含数据成员(变量),而 C++ 的结构体在此基础上还可以包含成员函数(方法),并且支持访问控制(public/private/protected)、继承等面向对象特性。
- C中通过:“结构体变量.成员” 访问数据;
- C++中可直接通过:“对象.成员函数()” 操作数据;
- C++ 的结构体可以像类一样使用,而 C 语言的结构体仅作为数据的聚合类型存在。
- C中定义结构体变量:struct 结构体名 变量名;
- C++中定义结构体变量:结构体名 变量名;
3、C++中的命名空间:
- 从c++的基本结构中发现,有一句代码,using namespace std; 如果没有这句代码,后面代码中使用的cin或者cout就会报错,这是什么原因?
- 这是因为 cin、cout 这些输入输出对象并不在 “全局命名空间” 中,而是定义在 C++ 标准库的 std 命名空间(namespace)里。如果没有 using namespace std;,就需要显式声明它们所属的命名空间(std::cin >> a;),否则编译器会找不到这些对象,从而报错。
- 在 C++ 中,“ :: ”是作用域解析运算符;
- 通过上面的问题引出了命名空间的概念;
- C++ 中命名空间的使用主要通过关键字“namespace”,可以把命名空间理解成 “文件夹”,用来分类存放代码中的变量、函数、类等,避免名字冲突。例如,你有两个文件都叫 “笔记.txt”,直接放在一起会混乱,但分别放在 “工作” 和 “生活” 两个文件夹里就清晰了 —— 命名空间就相当于这些文件夹。在C++语言中,命名空间就是为了解决命名重复的问题。
全名访问:命名空间名::内容(最安全)
打开整个命名空间:using namespace 命名空间名;(方便但谨慎使用)
单独导出:using 命名空间名::内容;(折中方案)
#include<iostream>
using namespace std;
//定义命名空间
namespace a {
void fun() {
cout << "fun_a" << endl;
}
}
// 给命名空间起别名
namespace b = a;
// 嵌套命名空间
namespace c {
void fun() {
cout << "fun_c" << endl;
}
namespace d{
void fun() {
cout << "fun_d" << endl;
}
}
}
// 在命名空间之外定义函数
namespace e {
void fun();
}
void e::fun() {
cout << "fun_e" << endl;
}
// 使用了命名空间声明之后,就可以不用在每次调用的时候再写“命名空间名::”
using namespace e;
int main() {
// 调用命名空间
a::fun();
b::fun();
c::fun();
c::d::fun();
e::fun();
/*
fun_a
fun_a
fun_c
fun_d
fun_e
*/
fun(); //fun_e
return 0;
}
- 命名空间多用于协助开发的时候。
4、文件分层:
- C 语言通过 “头文件(.h)+ 源文件(.c)” 的分离 和 目录结构规划 来模拟分层,本质是按功能模块拆分代码
- 头文件(.h):存放函数声明、宏定义、结构体 / 枚举类型定义等 “接口”,相当于模块的 “对外说明”。
- 源文件(.c):存放函数实现、静态变量等 “内部逻辑”,相当于模块的 “具体实现”。
- C++ 继承了 C 语言的 “头文件 + 源文件” 机制,同时增加了命名空间(namespace) 来强化逻辑分层,实现更灵活的代码组织:
- 命名空间解决了不同文件中同名函数 / 类的冲突问题,让文件分层更灵活(同一命名空间的代码可分散在多个文件,不同命名空间的代码可放在同一文件)。
- 注意:在写头文件的时候,为了防止头文件被重复包含(例如定义了3个头文件a.h、b.h、c.h,b和c中都调用了a,然后在主程序main.c中都调用了b和c,那么对于a中被调用的数据,在main中就是重复定义),最好是在每个头文件当中加上头文件保护符: #ifndef 头文件名大写_H、#define 头文件名大写_H、#endif;
- 案例:如果需要设置三个头文件,分别是Point.h、Circle.h、Rectangle.h,分别存放坐标结构体,圆的结构体(圆的相关函数声明)、矩形结构体(矩形的相关函数声明)。然后在function.cpp文件中写上圆的相关函数和矩形的相关函数,最后在主程序中调用。
// Circle.h
#ifndef CIRCLE_H
#define CIRCLE_H
#include"Point.h"
struct Circle {
Point point;
double r;
};
double zhou_chang(Circle r);
double mian_ji(Circle r);
#endif // !CIRCLE_H
// Rectangle.h
#ifndef RECTANGLE_H
#define RECTANGLE_H
#include"Point.h"
struct Rectangle {
Circle Point;
double lang;
double kuan;
};
double zhou_chang(Rectangle b);
double mian_ji(Rectangle b);
#endif // !RECTANGLE_H
// Point.h
#ifndef POINT_H
#define POINT_H
struct Point {
int x;
int y;
};
#endif // !POINT_H
// function.cpp
#include<iostream>
#include"Circle.h"
#include"Rectangle.h"
//using namespace std;
// 矩形
double zhou_chang(Rectangle b) {
double zc = 2 * (b.lang + b.kuan);
return zc;
}
double mian_ji(Rectangle b) {
double mj = b.lang * b.kuan;
return mj;
}
// 圆
double zhou_chang(Circle a) {
double zc = 2 * 3.14 * (a.r);
return zc;
}
double mian_ji(Circle a) {
double mj = 3.14 * (a.r) * (a.r);
return mj;
}
// main.cpp
#include<iostream>
#include"Circle.h"
#include"Rectangle.h"
using namespace std;
int main() {
// 结构体变量初始化
Circle a = { {3,4},3};
Rectangle b = { {4,5},3,4 };
// 调用
cout << "a周长:" << zhou_chang(a) << endl;
cout << "a面积:" << mian_ji(a) << endl;
cout << "b周长:" << zhou_chang(b) << endl;
cout << "b面积:" << mian_ji(b) << endl;
}
5、类的基础知识:
- 在说明类之前,先讲讲类的由来,c是面向过程的编程语言,其中基本单位是函数,c++既可以面向过程编程,也可以面向对象编程,在面向对象编程时,基本单位是类。
- 类的定义:举个例子比如你要描述“手机”,它有一些属性(特征):品牌、颜色、内存大小、电量;它的功能(行为):打电话、发短信、拍照;在 C++ 中,类(Class)就是对一类事物的 “模板” 或 “设计图”,它定义了某类事物共同的属性和功能。而我们实际使用的每一部具体手机,就是这个类的 “对象”(实例)。
- 使用类的方式:定义类(属性、功能)、创建对象、访问属性、调用功能;注意:在定义属性和功能的时候有三个关键字描述类当中成员(属性、功能)的权限(private是私有类,类外不可以访问、protected类外不可以访问,子类可以访问父类、public类内和类外都可以访问);需要注意,如果是写在类当中的函数,是不需要把类当中的属性作为形参传递的,这体现了类的封装性;下面是一个定义类的例子:
#include <iostream>
using namespace std;
class Rect
{
// 设定两个私有的属性,矩形的长和宽,属性名的命名规范:m_类型缩写_属性(首字母大写其他字母小写)
private: // 私有权限
int m_nLength;
int m_nWidth;
public: // 公有权限
// 方法实际上就是声明在类中的函数
int getLength();
int getWidth();
void setLength(int len);
void setWidth(int wid);
int getArea();
int getPerimeter();
};
// 在类外定义方法
// 方法类型 类名::方法名(){函数体}
int Rect::getLength()
{
return m_nLength;
}
int Rect::getWidth()
{
return m_nWidth;
}
int Rect::getArea()
{
return m_nLength * m_nWidth;
}
int Rect::getPerimeter()
{
return 2 * (m_nLength + m_nWidth);
}
void Rect::setLength(int len)
{
m_nLength = len;
}
void Rect::setWidth(int wid)
{
m_nWidth = wid;
}
int main()
{
// 创建一个对象
// 类名 对象名;
Rect r1;
r1.setLength(100);
r1.setWidth(50);
cout << r1.getArea() << endl;
return 0;
}
总结:
- 通过C和C++对比着学习,可以发现,C++本质就是对C的扩展,扩展了C不能面向对象编程的问题。C++的这一拓展也导致其编程范式和一些数据结构的书写规范有了区别,比如结构体在c中的定义和书写是一种方式,在C++中就更偏向于类的方式来书写结构体了。
- 还有就是在给类定义时如何把基本的结构写清楚,要知道在类中定义功能(函数)的时候,如果需要把属性作为形参传入,可以不用写形参,直接在功能当中使用就可以了,这体现了类的封装性。
- 在进行文件分层时,要把每个头文件中要写上头文件保护符;