c++ class struct同名_从C过渡到C++需要了解的新特性

2023-05-16

C语言是一门很底层的语言,对C语言的学习能帮助我们建立起对计算机底层原理的初步认识。但在C语言诞生的年代,受制于机器性能,程序设计语言更侧重于运行效率。到了今天,机器性能突飞猛进,运行效率已不是主要的矛盾。当今流行的几门面向对象程序设计语言都更侧重于提高编程效率。

本文将向你介绍一门经典的面向对象语言——C++

C++很强大,但C++也很庞大。本文是笔者结合各路资料和自身学习经历整理而得,希望能帮助你了解从C过渡到C++的重点。

第一个C++程序

#include 
using namespace std; //编译指令

int main() {
    cout <"Hello World!" <endl;
    return 0;
}

头文件

在C语言的传统中,头文件使用扩展名.h

//C风格
#include 

老式的C++延续了这个传统

//老式C++
#include 

而新式的C++抛弃了这个传统,转而去掉了.h

//新式C++
#include 

C++将部分C头文件转换为C++头文件并重新命名:去掉.h并加上前缀c

#include 

在代码中包含cstdio,C++程序中也可以出现scanf()printf()

对于纯粹的C++头文件(比如iostream)来说,去掉.h不只是形式上的变化,还可能包含了命名空间

风格约定示例CC++命名空间
C.h结尾stdio.h×
老式C++.h结尾iostream.h××
新式C++没有扩展名iostream×
转换后的C加上前缀c,没有扩展名cmath×

由于C使用扩展名.h来表示这是一个C头文件

如果C++头文件继续沿用这个拓展名,容易造成混淆

其中一种解决方案是另起一个扩展名,比如.hpp等。

但最后大家却一致同意不使用任何扩展名 可能是懒的选

编译指令

如果你使用的头文件是iostream,而不是iostream.h

那么你应该使用下面的语句来使iostream中的定义可用

using namespace std;

这句话告诉编译器,使用std这个命名空间,这样可以更方便的使用cout

关于命名空间的详细内容,留在了文章末尾的拓展部分。对于刚刚开始转向C++的萌新来说,最简单的方法就是背!记住要写这句话就好啦。

cout

cout <"Hello World!" <endl;

什么是cout

cout是一个预定义的对象(还没有提对象的概念,暂且把它当成一个东西、一个物件)。cout知道如何在屏幕上显示字符串、数字、单个字符等。

cout <56;
cout <' ';
cout <9.10;

这个<<又是什么?

<<是C位运算的左移操作符,用来操作位。

在C++有了新的用途——输出。箭头方向指明了信息流动的方向。

信息可以流向cout从屏幕上输出,也可以通过键盘输入流向某个变量

cin >> a;

箭头方向指向a,说明这个信息是流向了a

endl是一个控制符,相当于C里面的换行符\n

面向对象

C++融合了3种不同的编程方式

  • 面向过程

(Procedure Oriented Programming)

  • 面向对象

(Object Oriented Programming)

  • 泛型编程

(Generic Programming)

通过C语言的学习,相信你已经对面向过程有所了解。假如我们现在要用程序来模拟一次上课的场景,用面向过程的思维来设计的话就是:

  • if (时间 == 8:00) 上课
  • 老师讲课,学生听课
  • 进入循环
  • ……
  • if (时间 == 9:40) 下课

那如果我们要用面向对象的思维来模拟上课场景,该怎么做?

什么是对象

面向对象在台湾有另一种叫法——物件导向。所谓的对象其实就是我们常说的东西,对象可以是

  • 一个按钮
  • 一盏灯

一个对象由下面两样东西组成

  • 数据(Data)
  • 操作(Operations)

c04ed7587d9333d9a4ceecf6c74e50f7.png以一盏灯为例

  • Properties可以是灯丝,Status是灯是否开启、灯的颜色。
  • Operations可以是开关,这个操作会改变内部的Data

上面这副蛋图,蛋黄位置的DataOperations所包裹。想要碰到Data只能穿过Operaions

为什么要把Data包裹起来?举个栗子,电视机的外壳。这个外壳除了让产品更好看之外,更重要的是保护着电视内部的电气元件,通常外壳上都会贴一个警告标签

非专业人士请勿拆卸

显然厂家不希望用户拆开这个外壳,如果用户拆开了外壳,自己去拔插电视内部的线路,很有可能会损坏电视。

但这个外壳会影响我们的使用吗?当然不会,设计者留出了很多按钮开关来操作电视机,这些按钮开关就是我们说的Operations

在OOP的世界里,大致有两类程序员

  • 设计类的程序员(设计者)
  • 使用类的程序员(用户)

对于设计者来说,用户不能直接碰到内部数据。用户只能通过设计者给出的接口来访问内部数据。

对于用户来说,用户关心的是功能而不是原理。正如电视机的按钮,按下就可以启动,用户不需要了解其中的电子电路知识。设计者对用户屏蔽了具体实现细节。

把数据和数据的操作放在一起,形成有机联系,叫做封装。

所以,如果我们要用面向对象思维来模拟一个上课过程,我们要做的就是写对象

  • 一个老师对象
  • 若干个学生对象

这些对象的互动就是上课的过程。

从对象到类

  • class

a collection of things sharing a common attribute.

  • object

entity

object是实体,class是概念

小明是一个学生对象,他的名字是 小明,期末考成绩90

从小明身上抽象出学生的一些共有属性,可以得到下面的类

class Student{
    int sid;
    int score;
};

另外有一个学生对象小红,她也会有学生类里面的属性

所以,class定义了object该长什么样,而object里面有具体的属性。

OOP的五项原则

  1. Everything is an object.
  2. A program is a bunch of objects telling each other what to do by sending messages. 程序是由一堆互相之间传递消息的对象组成的。与之对应的是,面向过程语言(例如C)设计出来的程序是由一堆函数组成的。
  3. Each object has its own memory made up of other objects. 每一个对象有他自己的内存,这些对象里面还可以有对象。
  4. Every object has a type. 每个对象有一个自己的类型。
  5. All objects of a particular type can receive the same messages.

一个特定类型的对象可以接受相同的信息。反过来也可以说,能接受相同消息的对象可以认为是一样的对象。

结构体和类

//这是一个C结构体的标签
struct Student{
    int sid;
    int score;
};

用C语言定义这样一个结构体要在前面加struct

//C风格的定义
struct Student zs;

用C++定义时可以省略struct关键字

//C++风格的定义
Student zs;

同样的事情在C语言中实现要用到typedef

typedef struct _Student{
    int sid;
    int score;
}Student;

C++的结构体标签中还可以定义函数,这在C语言是不被允许的行为

//C++ 包含了操作方法的结构体
struct Student{
    int sid;
    int score;
    void haha( void ){
        cout <"haha..." <endl; 
    }
};

结合前面提到的OOP原理 C++的结构体可以放操作方法,有数据又有操作方法,所以C++中的结构体也是一个类class

struct Student{
    int sid;
    int score;
    // 操作方法
    void haha( void ){
        cout <"haha..." <endl; 
    }
};

上面的struct可以改写成下面的class

class Student{
public:
    int sid;
    int score;
    void haha( void ){
        cout <"haha..." <endl; 
    }
};

访问控制

public

如果声明类的某个成员为public,那么这个成员可以被类外面的语句、函数随便使用。以Student类为例,其全部成员声明为public,可以直接修改zs的数据

Student zs;
zs.sid = 007;

private

改写Student

class Student{
private:
    int sid;
    int score;
public:
    void haha( void ){
        cout <"haha..." <endl; 
    }
};

现在sidscore是这个类所私有的成员,此时不允许在外部用zs.sid = 007来修改内部数据,必须是类里面的成员才能修改它们。

class Student{
private:
    int sid;
    int score;
public:
    void haha( void ){
        cout <"haha..." <endl; 
    }
    void get_sid( void ){ //修改sid的接口
        cin >> sid;
    }
};

如果省略了publicprivate关键字

  • struct默认全部成员公开
  • class默认全部成员私有

布尔值

在C99之前,C语言是没有表示真假的布尔值,统统用int

#define TRUE  1
#define FALSE 0
int flag = 5 > 3;

flag的值只有01两种情况

C语言把非0当作真,0当作假,在C++也是一样的

//测试用例
if (3) {
    cout <1 <endl;
}

if (-2) {
    cout <2 <endl;
}
//屏幕输出
1
2

C++提供了bool型变量,bool只占用一个字节的空间,用truefalse这两个字面值常量来表示真假。

bool flag = true;
flag = false;

int类型的数据来给bool类型赋值时

  • 非0的认为是true
  • 0认为是false
bool flag = 123;
cout <endl;
//屏幕输出
1

auto

C++是一门强类型的语言,声明变量的时候必须清楚地知道表达式的类型,然而要做到这一点并非容易。auto关键字让编译器自己去推断类型。和int这些特定类型不同,auto定义的变量必须有初始化。

auto a = 3;
a /= 2;
cout <endl;
//屏幕输出
1

3来给a初始化,编译器推断aint

auto a = 3.0;
a /= 2;
cout <endl;
cout <sizeof(a) <endl;
//屏幕输出
1.5
8

3.0a初始化,编译器推断出adouble类型。

引用 Reference

引用是已经定义的变量的别名

int b = 1;
int &r = b;

声明引用变量时必须进行初始化定义引用时,引用一旦创建好之后引用来源不能改变。

引用示例

C函数的参数传递,要么是值传递,要么是指针传递。

//C版本的交换函数
void swaq( int *a, int *b ) {
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}

C++的参数传递不仅仅是值,还可以是引用。所以可以把交换函数的参数改为引用。

void swaq(int& a, int& b) {
    int temp;
    temp = a;
    a = b;
    b = temp;
}

在C代码中。看到func(a)可以放心的肯定func()得到的是一个值的拷贝,a原本的值不会被修改。

而到了C++,参数传递还可以传引用,看到func(a)不能简单地下结论。

如果你不希望参数引用的变量被修改,应该使用const

void func(const int& a);

右值引用

使用下面的语句给一个右值创建引用

const int& r = 123;
int&& r = 123; //右值引用

简要了解即可

赋值

C++可以编译下面的代码

(a = 3) = 666;
//屏幕输出
666
  • 在C语言中,(a = 3)的值为33是一个常量,常量不能做左值
  • 在C++,(a = 3)a的引用,可以继续赋值。

基于范围的for循环

int a[5] = { 1,2,5,3,4 };
for (int i : a) {
    cout <' ';
}
//屏幕输出
1 2 5 3 4 

依次打印出数组a的5个元素

加上&表示引用后,可以修改元素的值

for (int& i : a) {
    i++;
}

for (int i : a) {
    cout <}
//屏幕输出
2 3 6 4 5 

动态内存分配

申请内存

malloc()接受一个数值,指明要申请几个字节

//C版本
int size = 10;
int* p;
p = (int*)malloc(sizeof(int) * size);

到了C++,程序员不用自己数数了,直接指明我要什么,程序自己计算要多少空间。

//C++版本
p = new int[size];

释放内存

//C版本
free(p);
//C++版本
delete[] p;

new了之后一定要记得delete,有借有还再借不难。

加上 [] 释放整个数组,而不是只释放p[0]

重载 Overload

函数重载

C++可以编译下面的代码

int add( int x, int y );
int add( int x, int y, int z );

有两个函数名相同但参数列表不同的函数,在编译时编译器就会选取符合参数列表的函数。

错误重载1:相同的原型

但下面的代码不能通过编译

int add( int x, int y );
int add( int y, int z );

虽然参数的名字不相同,但两者的原型是一样的,都是

int add( int, int );

无法重载。

错误重载2:重载返回类型

下面的代码也不能通过编译

int add( int x, int y );
void add( int x, int y );

仅仅是返回类型不一样,编译器无法判断究竟重载哪个函数。无法重载。

错误重载3:有歧义的重载

int add(int x, int y) {
    return x + y;
}

double add(double x, double y) {
    return x + y;
}

int main(void) {
    cout <1, 2.5) <endl;
    return 0;
}

main函数里调用add的语句有歧义。1可以转换为double,2.5也可以转换为int,编译器不知道该重载哪个。

操作符重载

现在有一个向量Vector

struct Vector {
    int x;
    int y;
};

向量的加法

+号只能对整数浮点数求值,通过重载运算符+,用+就可以实现向量加法

Vector operator +(const Vector& a, const Vector& b) {
    Vector c;
    c.x = a.x + b.x;
    c.y = a.y + b.y;
    return c;
}
int main() {
    Vector a, b;
    a.x = 3;
    a.y = 4;
    b.x = 1;
    b.y = 5;
    Vector sum;
    sum = a + b;
    cout <" " <endl;
    return 0;
}

lambda表达式

auto f = []( int a, int b )-> int{ return a + b; };

这是函数的一种写法

  • f是一个函数指针
  • ( int a, int b )是参数列表
  • {}前面写函数的返回类型
  • {}里面写函数的内容。

扩展

命名空间可以帮助我们避免不经意的名字定义冲突,以及使用库中相同名字导致的冲突。标准库定义的所有名字都在命名空间std中。通过命名空间使用标准库,当使用标准库中的一个名字时,必须使用::显式说明。

std::cout <"Hello World!" <std::endl;

要使用命名空间,头文件iostream不能有.husing编译指令使得std命名空间里的所有名称都可用

之后都不需要使用std::

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

c++ class struct同名_从C过渡到C++需要了解的新特性 的相关文章

  • CSS class 和 id 同名

    css class 和 id 同名有什么问题吗 就像文章 帖子页脚的 footer 和页面页脚的 footer 一样 不 完全可以接受 一个类是使用 a 来定义的 并且 ID 是使用定义的 因此 就浏览器而言 它们是两个完全独立的项目 唯一
  • 结构填充和包装

    考虑 struct mystruct A char a int b char c x struct mystruct B int b char a y 结构的大小分别为 12 和 8 这些结构是填充的还是包装的 何时进行填充或包装 Padd
  • 如何通过在切片上查找来从切片复制到数组

    我正在编写一个库来处理二进制格式 我有一个带有数组变量的结构 我想保留它以用于文档目的 我还需要从输入字节片中查找和判断 一些伪代码 type foo struct boo 5 byte coo 3 byte func main input
  • 在 foreach 循环中更改另一个结构内的结构

    打印以下代码 调用 MyMethod 时 0 0 0 1 我希望它打印 0 0 1 1 为什么是这样 Code private struct MyStruct public MyInnerStruct innerStruct private
  • Exit() 时是否调用基本对象析构函数?

    我意识到这个问题已经出现过几次 但我试图获得上述问题的明确答案 但我不断遇到相互矛盾的信息 我需要知道的是 当我使用 exit 时 基本类对象是否被破坏 我知道需要删除动态内存 但我的意思更像是 include
  • Python:如何“杀死”类实例/对象?

    我希望 Roach 类在达到一定量的 饥饿 时 死亡 但我不知道如何删除该实例 我的术语可能有误 但我的意思是 窗户上有大量 蟑螂 我希望特定的蟑螂完全消失 我会向您展示代码 但它很长 我将蟑螂类添加到策划者类蟑螂种群列表中 一般来说 每个
  • Python - 如何实现“可停止”线程?

    已经发布解决方案了here https stackoverflow com questions 323972 is there any way to kill a thread in python创建一个可停止的线程 但是 我在理解如何实施
  • Vaadin:在表中显示列表

    我需要显示表中列表中包含的所有值 例如 class Person String name String age List
  • 结构体到磁盘的高效 Go 序列化

    我的任务是将 C 代码替换为 Go 而且我对 Go API 还很陌生 我正在使用 gob 将数百个键 值条目编码到磁盘页面 但 gob 编码有太多不需要的膨胀 package main import bytes encoding gob f
  • java中如何找到class文件的包

    我正在编写一个使用 class 文件的 java 程序 我希望能够读取文件系统上的 class 文件 使用 InputStream 并确定它所在的包 该 class 文件可能不在一个好的包目录结构中 它可能位于某个随机位置 我怎样才能做到这
  • 在 PHP 中使用可变变量是不好的做法吗?

    例如 一个简单的MVC类型系统 api class method使用重写为 PHP 变量 htaccess nginx conf 然后做类似的事情
  • 将 WPF 控件类作为模板类

    有没有办法让 WPF UserControl 类成为具有模板类型的类 例如 public partial class MyControl UserControl 应该 public partial class MyControl
  • 如何从另一个类访问变量而不创建新对象

    我过长的标题说明了一切 我希望能够从另一个类访问变量而不创建新对象 目前我知道如何访问另一个类的变量的唯一方法是 Control control new Control int dirtCount control dirtCount 然而
  • 什么时候需要使用 new 来初始化 F# 类型?

    给定一个类 例如 type MyClass member this Greet x printfn Hello s x 使用初始化实例是否合适 let x new MyClass 或没有new 另外 什么时候使用new构造函数比 a 更有用
  • 如何重载“新”方法?

    我刚刚开始学习 Rust 我想知道是否有方法重载方法 首先 我创建了一个结构并使用 impl 来实现基本的 新 方法 然后我想添加带有一些参数的 新 方法 并且我尝试使用 Trait 来实现这一点 以下代码已成功编译 但是当我尝试将 new
  • 在 C++ 中从另一个数组初始化结构内的数组[关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 help reopen questions struc
  • 类外函数

    我只是想告诉你 我是 OOP 的新手 这对我来说相当困难 但这是我的代码 class functions function safe query string string mysql escape string htmlspecialch
  • -[MyClassName copyWithZone:] 无法识别的选择器发送到实例

    我的应用程序崩溃了 原因是 MyClassName copyWithZone 无法识别的选择器发送到实例 我有两节课 假设 Class1 和 Class2 Class1 看起来像 Class1 h interface Class1 NSOb
  • 如何在Python中正确声明ctype结构+联合?

    我正在制作一个二进制数据解析器 虽然我可以依靠 C 但我想看看是否可以使用 Python 来完成该任务 我对如何实现这一点有一些了解 我当前的实现如下所示 from ctypes import class sHeader Structure
  • 为什么 C++ 不允许匿名结构?

    某些 C 编译器允许匿名联合和结构作为标准 C 的扩展 这是一些语法糖 偶尔会很有帮助 阻止其成为标准一部分的理由是什么 有技术障碍吗 哲学的 或者只是没有足够的需要来证明它的合理性 这是我正在谈论的内容的示例 struct vector3

随机推荐