【C++】从C语言到C++学习指南

ops/2024/11/24 22:43:32/

如果你也是从C语言一路过来的,那么请一起看下去吧!

文章目录

  • 面型对象程序设计
  • C++基础
    • C和C++一些语法区别
    • C++在非对象方面对C语言的扩充
    • C++的一些标准(兼容旧标准)

首先,在C++的学习中,我们要时刻清醒一点:虽然C++与C语言有很大区别,但C++是C语言的扩充

说到C++和C语言的区别,那也是面向对象和面向过程的区别,这时你的老师可能会这么给你讲:

在这里插入图片描述

可是学了C语言没学过C++的人发问了:我们在学面向过程设计的时候,说“数据和操作往往都是分离的”,数据是米饭,操作是米饭上盖的菜,既然如此面向过程那不应该是盖饭吗?而面向对象具有封装的特点更是炒饭呀!(老师气的脸都绿了)

只有当你亲口尝了这两种饭菜,你才会知道盖饭中的菜是什么米是什么,炒饭中的菜是什么米是什么,以及他们的口味。所以,当你正确的理解了这个比喻,你便能真正认识C语言与C++,面向过程和面向对象,那么请跟随我一起去寻找我们的答案吧~

面型对象程序设计

面向过程是这样的:

img

一个程序是一道菜,面向过程这道菜的特点是:先从整体角度全面看待问题,然后列出解决问题需要的步骤,再逐步去完善。

面向对象是这样的:

img

面向对象这道菜有两个主要的结构特点:①程序一般由类的定义(属性)和类的使用(行为)两部分组成;②程序中的一切都是通过向对象发送消息来实现的,对象接收到消息后,启动有关方法完成相应的操作。

  • 对象:描述其属性的数据以及对这些数据施加的一组操作封装在一起构成的统一体。对象可认为是数据+操作。
  • :类是具有相同的数据和相同的操作的一组对象的集合。
  • 消息传递:对象之间的交互。
  • **方法:**对象实现的行为称为方法

面向对象程序设计的基本特征:抽象、封装、继承、多态。


理清了面向对象和面向过程之间的区别后,然我们来了解一下——从C语言到C++~

C++基础

C++的产生和特点

C++是美国贝尔实验室的Bjarne Stroustrup博士在C语言的基础上,弥补了C语言存在的一些缺陷,增加了面向对象的特征,于1980年开发出来的一种面向过程性与面向对象性相结合的程序设计语言。最初他把这种新的语言称为“含类的C”,到1983年才取名为C++。

相比C语言,C++的主要特点就是增加了面向对象机制!

一个简单的C++示例程序

#include<iostream>                 //编译预处理命令
using namespace std;               //使用命名空间void Log(const char* message) {    //函数定义cout << message << endl;
}int main() {Log("Hello World!");cin.get();
}

C和C++一些语法区别

1.三目运算符:在C语言中返回的是一个常量,是不能被赋值的;而C++中返回的是变量,可以被赋值

2.C++中的函数必须要写返回值类型

3.在全局下,C++不允许int a;int a=10;等这种重定义二义性操作

4.在C++中不要返回局部变量或临时变量的地址,虽然C++能够运行变量出作用域之后再使用一次,即C++允许在代码块中的任何地方声明局部变量。

5.C语言中const修饰的变量不能定义数组大小,而C++中可以

const int a=10;
float arr[a];
12

6.C++中定义结构体类型变量可以不用写结构体类型名

7.关于C语言有没有bool类型:C语言的布尔类型(bool)

C++在非对象方面对C语言的扩充

(1)输入和输出

int i;
float f;
cin >> i;
cout << f;
------------
scanf("%d", &i);
printf("%f", f);
----------------
连续读入
cin >> a >> b >> c;

【cin】

  • 在默认情况下,运算符“>>”将跳过空白符,然后读入后面与变量类型相对应的值。因此,给一组变量输入值时可用空格符、回车符、制表符将输入的数据间隔开。
  • 当输入字符串(即类型为string的变量)时,提取运算符“>>”的作用是跳过空白字符,读入后面的非空白字符,直到遇到另一个空白字符为止,并在串尾放一个字符串结束标志‘\0’。

(2)const修饰符

在C语言中,习惯使用#define来定义常量,例如#define PI 3.14,C++提供了一种更灵活、更安全的方式来定义常量,即使用const修饰符来定义常量。例如const float PI = 3.14;

const可以与指针一起使用,它们的组合情况复杂,可归纳为3种:指向常量的指针、常指针和指向常量的常指针。

  • 指向常量的指针:一个指向常量的指针变量。

    const char* pc = "abcd";
    该方法不允许改变指针所指的变量,即pc[3] = ‘x';   是错误的,
    但是,由于pc是一个指向常量的普通指针变量,不是常指针,因此可以改变pc所指的地址,例如pc = "ervfs";
    该语句付给了指针另一个字符串的地址,改变了pc的值。
    123456
    
  • 常指针:将指针变量所指的地址声明为常量

    char* const pc = "abcd";
    创建一个常指针,一个不能移动的固定指针,可更改内容,如pc[3] = 'x';
    但不能改变地址,如pc = 'dsff';  不合法
    12345
    
  • 指向常量的常指针:这个指针所指的地址不能改变,它所指向的地址中的内容也不能改变。

    const char* const pc = "abcd";
    内容和地址均不能改变
    12
    

说明:

  1. 如果用const定义整型常量,关键字可以省略。即 const in bufsize = 100const bufsize = 100等价;
  2. 常量一旦被建立,在程序的任何地方都不能再更改。
  3. 与#define不同,const定义的常量可以有自己的数据类型。
  4. 函数参数也可以用const说明,用于保证实参在该函数内不被改动。

(3)void型指针

void通常表示无值,但将void作为指针的类型时,它却表示不确定的类型。这种void型指针是一种通用型指针,也就是说任何类型的指针值都可以赋给void类型的指针变量

需要指出的是,这里说void型指针是通用指针,是指它可以接受任何类型的指针的赋值,但对已获值的void型指针,对它进行再处理,如输出或者传递指针值时,则必须再进行显式类型转换,否则会出错。

    void* pc;int i = 123;char c = 'a';pc = &i;cout << pc << endl;         //输出指针地址006FF730cout << *(int*)pc << endl;  //输出值123pc = &c;cout << *(char*)pc << endl; //输出值a

(4)内联函数

在函数名前冠以关键字inline,该函数就被声明为内联函数。每当程序中出现对该函数的调用时,C++编译器使用函数体中的代码插入到调用该函数的语句之处,同时使用实参代替形参,以便在程序运行时不再进行函数调用。引入内联函数主要是为了消除调用函数时的系统开销,以提高运行速度。

说明

  • 内联函数在第一次被调用之前必须进行完整的定义,否则编译器将无法知道应该插入什么代码
  • 在内联函数体内一般不能含有复杂的控制语句,如for语句和switch语句等
  • 使用内联函数是一种空间换时间的措施,若内联函数较长,较复杂且调用较为频繁时不建议使用
#include <iostream>
using namespace std;inline double circle(double r)  //内联函数
{double PI = 3.14;return PI * r * r;
}int main() 
{for (int i = 1; i <= 3; i++)cout << "r = " << i << " area = " << circle(i) << endl;return 0;
}

使用内联函数替代宏定义,能消除宏定义的不安全性

(5)带有默认参数值的函数

当进行函数调用时,编译器按从左到右的顺序将实参与形参结合,若未指定足够的实参,则编译器按顺序用函数原型中的默认值来补足所缺少的实参。

void init(int x = 5, int y = 10);
init (100, 19);   // 100 , 19
init(25);         // 25, 10
init();           // 5, 10
1234
  • 在函数原型中,所有取默认值的参数都必须出现在不取默认值的参数的右边

    int fun(int a, int b, int c = 111);
    1
    
  • 在函数调用时,若某个参数省略,则其后的参数皆应省略而采取默认值。不允许某个参数省略后,再给其后的参数指定参数值。

(6)函数重载

在C++中,用户可以重载函数。这意味着,在同一作用域内,只要函数参数的类型不同,或者参数的个数不同,或者二者兼而有之,两个或者两个以上的函数可以使用相同的函数名。

#include <iostream>
using namespace std;int add(int x, int y)
{return x + y;
}double add(double x, double y)
{return x + y;
}int add(int x, int y, int z)
{return x + y + z;
}int main() 
{int a = 3, b = 5, c = 7;double x = 10.334, y = 8.9003;cout << add(a, b) << endl;cout << add(x, y) << endl;cout << add(a, b, c) << endl;return 0;
}

说明:

  • 调用重载函数时,函数返回值类型不在参数匹配检查之列。因此,若两个函数的参数个数和类型都相同,而只有返回值类型不同,则不允许重载。

    int mul(int x, int y);
    double mul(int x, int y);
    12
    
  • 函数的重载与带默认值的函数一起使用时,有可能引起二义性。

    void Drawcircle(int r = 0, int x = 0, int y = 0);
    void Drawcircle(int r);
    Drawcircle(20);
    123
    
  • 在调用函数时,如果给出的实参和形参类型不相符,C++的编译器会自动地做类型转换工作。如果转换成功,则程序继续执行,在这种情况下,有可能产生不可识别的错误。

    void f_a(int x);
    void f_a(long x);
    f_a(20.83);
    

(7)作用域标识符::

通常情况下,如果有两个同名变量,一个是全局的,另一个是局部的,那么局部变量在其作用域内具有较高的优先权,它将屏蔽全局变量。

如果希望在局部变量的作用域内使用同名的全局变量,可以在该变量前加上“::”,此时::value代表全局变量value,“::”称为作用域标识符。

#include <iostream>
using namespace std;int value;   //定义全局变量valueint main() 
{int value;  //定义局部变量valuevalue = 100;::value = 1000;cout << "local value : " << value << endl;cout << "global value : " << ::value << endl;return 0;
}

(8)强制类型转换

可用强制类型转换将不同类型的数据进行转换。例如,要把一个整型数(int)转换为双精度型数(double),可使用如下的格式:

int i = 10;
double x = (double)i;int i = 10;
double x = double(i);
12345

以上两种方法C++都能接受,建议使用后一种方法。

(9)new和delete运算符

程序运行时,计算机的内存被分为4个区:程序代码区、全局数据区、堆和栈。其中,堆可由用户分配和释放。C语言中使用函数malloc()free()来进行动态内存管理。C++则提供了运算符newdelete来做同样的工作,而且后者比前者性能更优越,使用更灵活方便。

指针变量名 = new 类型int *p;p = new int;
delete 指针变量名delete p;
12345

下面对new和delete的使用再做一下几点说明:

  • 用运算符new分配的空间,使用结束后应该用也只能用delete显式地释放,否则这部分空间将不能回收而变成死空间。

  • 在使用运算符new动态分配内存时,如果没有足够的内存满足分配要求,new将返回空指针NULL)。

  • 使用运算符new可以为数组动态分配内存空间,这时需要在类型后面加上数组大小。

    指针变量名 = new 类型名[下标表达式];
    int *p = new int[10];
    12
    

    释放动态分配的数组存储区时,可使用delete运算符。

    delete []指针变量名;
    delete p;
    12
    
  • new 可在为简单变量分配空间的同时,进行初始化

    指针变量名 = new 类型名(初值);
    int *p;
    p = new int(99);
    ···
    delete p;
    

(10)引用

引用reference)是C++对C的一个重要扩充。变量的引用就是变量的别名,因此引用又称别名

类型 &引用名 = 已定义的变量名
1

引用与其所代表的变量共享同一内存单元,系统并不为引用另外分配存储空间。实际上,编译系统使引用和其代表的变量具有相同的地址。

#include <iostream>
using namespace std;
int main() 
{int i = 10;int &j = i;cout << "i = " << i << " j = " << j << endl;cout << "i的地址为 " << &i << endl;cout << "j的地址为 " << &j << endl;return 0;
}

上面代码输出i和j的值相同,地址也相同。

  • 引用并不是一种独立的数据类型,它必须与某一种类型的变量相联系。在声明引用时,必须立即对它进行初始化,不能声明完成后再赋值。
  • 为引用提供的初始值,可以是一个变量或者另一个引用。
  • 指针是通过地址间接访问某个变量,而引用则是通过别名直接访问某个变量。

引用作为函数参数、使用引用返回函数值

#include <iostream>
using namespace std;void swap(int &a, int &b)
{int t = a;a = b;b = t;
}int a[] = {1, 3, 5, 7, 9};int& index(int i)
{return a[i];
}int main() 
{int a = 5, b = 10;//交换数字a和bswap(a, b);cout << "a = " << a << " b = " << b << endl;cout << index(2) << endl;   //等价于输出元素a[2]的值index(2) = 100;             //等价于将a[2]的值赋为100;cout << index(2) << endl;return 0;
}

对引用的进一步说明

  • 不允许建立void类型的引用
  • 不能建立引用的数组
  • 不能建立引用的引用。不能建立指向引用的指针。引用本身不是一种数据类型,所以没有引用的引用,也没有引用的指针。
  • 可以将引用的地址赋值给一个指针,此时指针指向的是原来的变量。
  • 可以用const对引用加以限定,不允许改变该引用的值,但是它不阻止引用所代表的变量的值。

C++的一些标准(兼容旧标准)

1.以初始化列表的方式赋值

int c{2};
int d{(int)3.3};
int arr1[6]{1,2,3};

2.空指针

int *p=NULL;//旧标准
int *p1=nullptr;//新标准

3.自动类型

auto x=10.6;//根据初始化赋值的类型决定变量的类型

4.decltype的使用:可以理解为 复制类型

int n=123;
decltype(n) m=100;//定义一个和变量n一样类型的变量m
decltype((n)) k=n;//给变量n取一个别名k,类似于引用 int& k=n;
123

5.给数据类型取别名

typedef int HP;//旧
using MP = int;//新
typedef void(*pFun)();//旧
using PFun = void(*)();//新
typedef char str[10];//旧
using Str = char[10];//新
123456

6.新的for循环语法规则

#include<iostream>
int main() {int arr2[10] = { 1,2,3,4,5,6,7,8,9,0 };for (int i = 0; i < 10; i++) {std::cout << arr2[i] << '\t';}std::cout<<std::endl;//新for (auto i : arr2) {std::cout << i << '\t';}
}

这个方法只能用来遍历数组或者容器,i所代表的是里面存储的数据元素。指针不能这样遍历。


好啦,下面我们就进入C++的系统学习吧!(笔者会陆续更新哦)

三大特点:

①封装——类和对象

②继承与派生

③多态性与虚函数

运算符重载

函数模版与类模版

C++的输入和输出

异常处理和命名空间

STL标准模板库


http://www.ppmy.cn/ops/136419.html

相关文章

周期法频率计的设计

目录 周期法频率计 分析&#xff1a; 设计过程&#xff1a; 周期法频率计 对于低频信号&#xff0c;应用周期法进行测频。周期法测频的基本原理是&#xff1a;应用标准频率信号统计被测信号两个相邻脉冲之间的脉冲数&#xff0c;然后通过脉冲数计算出被测信号的周期&#xff…

【遵守孤儿规则的External trait pattern】

遵守孤儿规则的External trait pattern Rust中的孤儿规则trait约定俗成的impl路径内部类型实现外部trait(impl External Trait for Internal Type)外部类型实现内部trait(impl Internal Trait for External Type) Rust中的孤儿规则 孤儿规则&#xff0c;Rust限制了开发者&…

shell(三)

声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&a…

前端项目支持tailwindcss写样式

安装 npm install -D tailwindcss npx tailwindcss init配置 tailwind.config.js //根据个人需求填写&#xff0c;比如vue简单配置 /** type {import(tailwindcss).Config} */ module.exports {darkMode: "class",corePlugins: {preflight: false},content: [&quo…

react native 安装好apk后无法打开

react native 打包好apk安装完成&#xff0c;没有打开app按钮&#xff0c; 在AndroidManifest.xml中 <intent-filter><action android:name"android.intent.action.MAIN" /><category android:name"android.intent.category.LAUNCHER" /&…

利用 Python 和 Selenium 高效启动和管理 Chrome 浏览器

在自动化测试和网页抓取的过程中&#xff0c;Selenium 是最常用的工具之一。其强大的功能可以与浏览器无缝集成&#xff0c;实现复杂的操作。然而&#xff0c;为了提高效率和扩展性&#xff0c;尤其在处理大量任务时&#xff0c;我们可以通过定制化的方法启动 Chrome 浏览器并与…

从0开始分享一个React项目:React-ant-admin

​ 项目源码&#xff1a;https://gitee.com/kong_yiji_and_lavmi/react-ant-admin 项目介绍网站:https://z3web.cn/doc-react-ant-admin/guide/start.html 建议学完React基本知识后&#xff0c;此项目巩固和了解基本知识在项目中如何使用&#xff0c;以及项目架构。 在此基础上…

在win10环境部署opengauss数据库(包含各种可能遇到的问题解决)

适用于windows环境下通过docker desktop实现opengauss部署&#xff0c;请审题。 文章目录 前言一、部署适合deskdocker的环境二、安装opengauss数据库1.配置docker镜像源2.拉取镜像源 总结 前言 注意事项&#xff1a;后面docker拉取镜像源最好电脑有科学上网工具如果没有科学上…