目录
7.4 使用“重新解释强制类型转化”reinterpret_cast
几乎99%的初学者,包括很多已经耕耘多年的大佬,都是先学习C语言,再重新学习C++,这个过程,浪费了大量时间,因为有很多内容是完全重复的。从语言特性上说,C++本身就包含C语言的所有特性的,既然如此,我们能不能直接从C语言快速提升到C++呢?毕竟,C语言入门后,不想再写C++的Hello world了哦!这篇博客,总结了从C语言基础,快速掌C++的“捷径”。
本片博客,要求已经掌握C语言基础用法。
1. 输入输出
1.1 输入的区别
C语言的输入
#include <stdio.h>
int main(void) {
int x;
int ret = scanf("%d", &x);
// 输入成功,scanf返回成功输入数据的个数,这里为1
// 输入失败,scanf返回0,例如输入a时返回0
// 输入结束(遇到文件结束符,对于标准输入设备-键盘,按下ctrl+z),返回-1
return 0;
}
C++的输入:
#include <iostream>
int main(void) {
int x, y;
std::cin >> x >> y;
// 输入完成后,std:cin返回cin本身, 以便实现连续输入
return 0;
}
注意cin的返回值,当输入结束时,可以通过类型转换为false
#include <iostream>
int main(void) {
int x;
if (!(std::cin >> x)) {
std::cout << "输入失败!";
}
return 0;
}
1.2 输出的区别
C语言的输出:
#include <stdio.h>
int main(void) {
int x = 100;
printf("x=%d\n", x);
return 0;
}
C++的输出:
#include <iostream>
int main(void) {
int x = 100;
std::cout << "x=" << x << std::endl;
return 0;
}
补充:文件名以.c结尾,很多集成开发环境(例如VS)会自动使用c语言编译器来编译,文件名以.cpp结尾,自动以c++编译器来编译。
2. 命名空间
使用C语言开发大型项目时,一定要在开发之前,做好命名约定,避免不同模块之间的标识符同名。一共常见的用法,每个模块对外使用的接口,以这个模块名作为前缀。例如调制解调器模块modem的接口: modemInit(调制解调器的初始化), modemGetTemperature(获取调制解调器的温度)。而在C++中,可以使用命名空间来进行标识符的“隔离”。std是C++已经定义好的命令空间。
使用 using namespace 可以简化命令空间的使用。
#include <iostream>
using namespace std;
int main(void) {
int x;
cin >> x; // 不需要再写成 std::cin >> x;
cout << x; // 不需要再写成 std::cin << x;
return 0;
}
3. 字符串
C语言没有专用的字符串类型,直接使用char数组,或者char*来表示字符串。
#include <stdio.h>
#include <string.h>
int main(void) {
char name[32];
printf("您贵姓啊?");
scanf("%s", name);
strcat(name, "工");
printf("你使用什么语言哦?");
char language[16];
scanf("%s", language);
if (strcmp(language,"C++") ==0){
printf("%s大佬好!", name);
}
return 0;
}
C++直接使用string类型表示字符串。
#include <iostream>
#include <string>
using namespace std;
int main(void) {
string str;
cout << "您贵姓啊?";
cin >> str;
str = str + "工";
cout << str << ",出BUG了!";
cout << "你使用什么语言哦?";
string language;
cin >> language;
if (language == "C++") {
cout << "大佬好!" << endl;
}
return 0;
}
4. 代码风格
5. bool类型
C语言本身,并不支持bool类型,不能直接使用,除非自己定义:
#define bool int
#define false 0
#define true 1
或者直接包含头文件:
#include <stdbool.h>
C++可以直接使用bool类型。
6. 引用
对于C语言,把变量作为函数的参数传递给函数后,如果需要在这个函数内部修改这个变量的值,就需要把对应的参数定义为指针。如果这个变量本身就是一个指针,就需要把对应的参数定义为二级指针。而在C++中,只需要使用引用类型就可以实现。
C语言实现:
C++的引用:
#include <iostream>
void addSalary(int& salary) {
salary += 1000;
}
int main(void) {
int salary = 20000;
addSalary(salary);
std::cout << "salary=" << salary << std::endl;
return 0;
}
引用在本质上,也是通过指针实现的,但引用是由编译器在底层用指针来实现的,已经对C++程序员做了透明化处理,我们已经不需要关注底层的实现细节, 直接理解为“引用”就是被引用变量的“别名”。
7. 强制类型转换
7.1 C语言的强制类型转换
C语言的强制类型转换,非常简单粗暴,不管你同不同意,乐不乐意,直接转。
#include <stdio.h>
struct girl {
int age;
int size[3]; //三维
char name[16];
};
struct boy {
int age;
int weight; //体重:kg
int height; //身高:cm
char name[16];
};
void showGirl(struct girl* girl) {
printf("\n姓名:%s\n三维:%d,%d,%d\n年龄:%d\n",
girl->name, girl->size[0], girl->size[1], girl->size[2], girl->age);
}
void showBoy(struct boy* boy) {
printf("\n姓名:%s\n身高:%d\n体重:%d\n年龄:%d\n",
boy->name, boy->height, boy->weight, boy->age);
}
int main(void) {
struct girl girl1 = { 20, {80, 70, 90}, "小凤" };
struct boy boy1 = { 22, 80, 178, "老王" };
// 正常输出
showGirl(&girl1);
showBoy(&boy1);
// 使用强制类型转化输出不可预期的结果(实际上c语言编译器不需要使用代码强转)
showGirl((struct girl*)&boy1);
return 0;
}
执行效果如下:
7.2 C++的静态强制类型转换 static_cast
struct girl girl1 = { 20, {80, 70, 90}, "小凤" };
struct boy boy1 = { 22, 80, 178, "老王" };
// 使用静态强制类型转换,boy1转“伪娘”失败!(但是可以使用C语言的强制类型转换成功)
struct girl* p = static_cast<struct gilr*>(&boy1);
正因为转换失败,所以更安全!在C++中,一般的类型转换,建议使用 static_cast
7.3 C++的const_cast类型转换
C++中不能直接使用自由指针指向const常量:
const int x = 10;
int* p = &x; //错误!
此时只要使用const_cast去除const属性即可。
const int x = 10;
int* p = const_cast<int*>(&x); //使用const_cast去除const属性
*p = 20; //不会报错,(但实际上并没有被修改)
printf("%d\n", x); // 10
printf("%d\n", *p); // 20
7.4 使用“重新解释强制类型转化”reinterpret_cast
#include <iostream>
using namespace std;
int main()
{
int num = 0x00636261;//0x61是字符'a'的ASCII码
//char* p = static_cast<char*>(&num); //使用静态强制类型转换失败
char* p = reinterpret_cast<char*>(&num); //使用“重新解释强制类型转化”
cout << p << endl;
return 0;
}
其实这里直接使用C语言方式的强制类型转换,也能起到类似的效果。
7.5 使用动态类型转换dynamic_cast
dynamic_cast 用于具有共同祖先类的各个类层次间的向上和向下转换,但只适用于含有虚函数的类,否则讲导致编译失败。使用dynamic_cast转换失败时返回NULL,可以根据是否为空来判定是否转换成功。
#include <iostream>
using namespace std;
class Father {
public:
virtual void play() { std::cout << "K歌" << std::endl; }
private:
int money;
};
class Son :public Father {
public:
virtual void play() { std::cout << "吃鸡" << std::endl; }
void work() { std::cout << "working in " << addr << std::endl; }
private:
string addr = "China";
};
int main(void) {
//在实际项目开发中,往往并不知道p1和p2到底指向的是Father对象还是Son对象
//比如,很多对象是根据程序的运行情况,比如用户的交互情况来创建父类或者子类对象,最后都由父类指针所指向
Father* p1 = new Father;
Father* p2 = new Son;
Son* s;
s= (Son*)p2;
s->work(); // OK
//危险!将导致崩溃!直接使用C语言方式的强制类型转换是不安全的
//s = (Son*)p1;
//s->work();
// 使用静态类型转换,竟然转换成功!但依旧是不安全的!将导致崩溃
//s = static_cast<Son*>(p1);
//s->work();
// 使用动态类型转换,转换事变后,返回NULL
s = dynamic_cast<Son*>(p1);
if (s) {
s->work();
} else {
std:cout << "转化失败!" << std::endl;
}
return 0;
}
8. 函数重载
C语言不支持函数重载。
// demo.c
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int add(int a, int b, int c) { //编译报错!
return a + b + c;
}
int main(void) {
printf("%d", add(3, 5, 8));
return 0;
}
C++支持函数重载哦
//demo.cpp
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int add(int a, int b, int c) { //实现函数重载
return a + b + c;
}
int main(void) {
printf("%d", add(3, 5, 8));
return 0;
}
9. 内存分配
C语言内存分配: malloc 和 free
C++内存分配:new 和 delete
11. 异常处理
C语言的异常处理:
#include <stdio.h>
#include <assert.h>
int div(int a, int b) {
assert(b != 0);
return a / b;
}
int main(void) {
printf("%d\n", div(100, 0));
return 0;
}
C++异常处理:
#include <iostream>
using namespace std;
double division(int a, int b) {
if (b == 0) {
throw "Division by zero condition!"; //抛出异常
}
return (a / b);
}
int main() {
int x = 50;
int y = 0;
double z = 0;
try {
z = division(x, y);
cout << z << endl;
}
catch (const char* msg) {
cerr << msg << endl;
}
return 0;
}
12. 结构体和类
C语言的结构体,只含有数据,C++的类,是一个加强版的“结构体”,主要增加了以下特性:
1. 使用了面向对象思想,可以继承和派生(父类、子类、孙子类)
2. 使用了权限控制(public, private, protected)
3. 使用了构造函数和析构函数
13. 文件操作
C++使用IO流的方式操作文件,实际效果和C语言的文件操作相同。
14. auto自动类型推导
int x = 100;
auto y = 100;
auto在对C++容器的迭代器操作时,非常频繁,也非常方便。
15. for循环
#include <stdio.h>
#include <iostream>
int main() {
int score[10] = {90, 95, 88, 73, 84, 100, 50, 91, 80, 65};
// C语言用法,C++老式用法
for (int i = 0; i < 10; i++) {
printf("%d ", score[i]);
}
printf("\n");
// C++新方式1
for (auto x : score) {
printf("%d ", x);
x++; //x拷贝自score, x改变,对score数组无意向
}
printf("\n");
// C++新方式2
for (auto &x : score) {
printf("%d ", x);
x++; //x是对score中成员的引用, x改变,score数组被改变
}
printf("\n");
// C++新方式3
for (const auto& x : score) {
printf("%d ", x);
//x++; //不能改变x
}
printf("\n");
return 0;
}
16. 从函数指针到lambda表达式
#include <iostream>
void hello() {
std::cout << "hello world" << std::endl;
}
// 使用函数指针作为参数
void test(void(*p)()) {
p();
}
int main(void) {
//最简单的lambda表达式
auto f = [] {
std::cout << "你能看出我是一个函数吗?" << std::endl;
};
f(); //lambda表达式也可以直接调用
test(hello); //使用函数指针
test(f); //使用lambda表达式
return 0;
}
17. 从手写轮子到各种容器
C++常用容器:vector, list, deque双端队列容器), set, map, stack(栈), queue(队列容器).
今天的分享就到这里了,大家要好好学C语言/C++哟~
欢迎转行和学习编程的伙伴,利用更多的资料学习成长比自己琢磨更快哦!
对于准备学习C/C++编程的小伙伴,如果你想更好的提升你的编程核心能力(内功)不妨从现在开始!
整理分享(多年学习的源码、项目实战视频、项目笔记,基础入门教程)加下方群获取哦~
C语言C++编程学习交流圈子,QQ群:763855696【点击进入】
C语言从入门到精通(C语言入门C语言教程C语言零基础C语言基础C语言学习C