从零开始的c++之旅——入门基础(上)

C++的第一个程序

当我们学习一门语言的第一件事毋庸置疑,是从我们的“hello world”开始的

虽然说C++兼容C语⾔绝⼤多数的语法,所以C语⾔实现的hello world依旧可以运⾏,只需要把定义⽂件代码后缀改为.cpp即可

但是C++有⼀套⾃⼰的输⼊输出,严格说C++版本的hello world应该是这样写的

#include<iostream>
using namespace std;
int main()
{
	cout << "hello world" << endl;
	return 0;
}

一开始看这段代码绝对是一头雾水,不要紧,接下来我们会对C++的一些语法规则进行讲解,相信讲解完之后,在回头看这段代码,一定是轻松拿捏。

1.命名空间

1.1 关键字namespace

我们在c语言的学习过程中,不可避免的会出现大量的变量,函数和类。这些变量,函数和类的名字都存在于全局作用域中,若一不小心就可能产生命名冲突,以下是一个例子

#include <stdlib.h>
int rand = 10;
int main()
{
    printf("%d\n", rand);
    return 0;
}

这样的代码会编译报错,因为rand在c语言中是一个函数名,我们这里创建了一个rand名称的变量,编译器会报错说产生了重定义

而在 c++中则引入了namesapce的关键字,很好的帮助我们解决了类似的命名冲突问题。

1.2 namespace的定义

谈namespace之前我们先提及域的概念

在c++中域有函数局部域,全局域,命名空间域,类域;域影响的是编译时语法查找⼀个变量/函数/类型 出处(声明或定义)的逻辑,所有有了域隔离,名字冲突就解决了。

namespace的作用就是让定义一块命名空间域,这个域跟全局域是相互独立的,因此不同的域可以定义相同的变量

相当于小明和小红是两个不同的人,但是他们可以用同一个型号的手机,手机的型号虽然是一样的,但是拥有者不同

同样的,虽然在两个域中定义了相同名字的变量,但是这两个变量的作用域是不相同的,所以有了域隔离,名字冲突就解决了。

1.3 namespace的特点

用namespace定义命名空间,我们只需要后⾯跟上命名空间的名字,然后接⼀对{}即可,{}中

即为命名空间的成员。命名空间中可以定义变量/函数/类型等。通过这个方法我们就可以上述代码rand重定义的问题

#include <stdio.h>
namespace space
{
    int rand = 10;
}
int main()
{
    printf("%d\n", space::rand);
    return 0;
}

namespace的使用还需要注意以下几点:
        1.namespace只能定义在全局,当然他还可以嵌套定义。

        2.项⽬⼯程中多⽂件中定义的同名namespace会认为是⼀个namespace,不会冲突。

        3.C++标准库都放在⼀个叫std(standard)的命名空间中。

1.4 命名空间的使用

我们c++在编译查找函数声明是,默认只在局部或者全局查找,不会到命名空间⾥⾯去查找。所以下⾯程序会编译报错。

#include<stdio.h>
namespace bit
{
int a = 0;
int b = 1;
} 
int main()
{
    // 编译报错:error C2065: “a”: 未声明的标识符
    printf("%d\n", a);
    return 0;
}

所以我们要使⽤命名空间中定义的变量/函数,有三种⽅式:

        • 指定命名空间访问,项⽬中推荐这种⽅式。

// 指定命名空间访问
int main()
{
    printf("%d\n", N::a);
    return 0;
}

        • using将命名空间中某个成员展开,项⽬中经常访问的不存在冲突的成员推荐这种⽅式。相当于将命名空间域中的变量变成了全局变量

// using将命名空间中某个成员展开
using N::b;
int main()
{
    printf("%d\n", N::a);
    printf("%d\n", b);
    return 0;
}

        • 展开命名空间中全部成员,项⽬不推荐,冲突⻛险很⼤,⽇常⼩练习程序为了⽅便推荐使⽤。

/ 展开命名空间中全部成员
using namespce N;
int main()
{
printf("%d\n", a);
printf("%d\n", b);
return 0;
}

2. C++输⼊&输出

<iostream> 是 Input Output Stream 的缩写,是标准的输⼊、输出流库,定义了标准的输⼊、输出对象。这些IO流的输出流输入流的概念,现在理解起来还是会相对比较抽象,在后续博客更新中还会详细讲解,这里简单的提及一下;

std::cin 是 istream 类的对象,它主要⾯向窄字符(narrow characters (of type char))的标准输⼊流。        可以理解为c语言的scanf。

std::cout 是 ostream 类的对象,它主要⾯向窄字符的标准输出流。
               可以理解为c语言的printf。

std::endl 是⼀个函数,流插⼊输出时,相当于插⼊⼀个换⾏字符加刷新缓冲区。
               可以理解为打印一个换行符”\n“,但在windows系统中的换行符是”\n“,
               而别的操作系统就不一定是了,但用std::endl可以做到自动识别,因
               此用std::endl的兼容性会比”\n“要好。

cout/cin/endl等都属于C++标准库,C++标准库都放在⼀个叫std(standard)的命名空间中,所以要通过命名空间的使⽤⽅式去⽤他们。而且vs系列编译器没有包含<stdio.h>,也可以使⽤printf和scanf,这是因为在包含<iostream>间接包含了。其他编译器可能会报错。

<<是流插⼊运算符,>>是流提取运算符。(C语⾔还⽤这两个运算符做位运算左移/右移)

在c++中输入和输出会相对c语言较为方便一点。因为我们不需要像printf/scanf输⼊输出时那样,需要⼿动指定格式,C++的输⼊输出可以⾃动识别变量类型(本质是通过函数重载实现的,在以后的章节中会讲到),其实最重要的是C++的流能更好的⽀持⾃定义类型对象的输⼊输出。

#include<iostream>
using namespace std;
int main()
{
	int a = 0;
	double b = 0.1;
	char c = 'x';
	cout << a << " " << b << " " << c << endl;
	std::cout << a << " " << b << " " << c << std::endl<<std::endl;

	scanf("%d %lf %c", &a, &b, &c);
	printf("%d %lf %c\n\n", a, b, c);

	cin >> a >> b >> c;
	cout << a <<" " << b << " " << c << endl;
	return 0;
}

 可以发现,我们最后两行代码,在使用cin接收时并没有声明其输入输出类型,而却可以对应的将不同类型的变量打印出来。

3. 缺省参数

缺省参数是声明或者定义函数时为函数参数指定一个缺省值。

当我们调用函数时候,若没有传递指定参数,那么默认采用我们形参的缺省值,否则采用指定的实参。(有些地方缺省参数也叫默认参数)

#include<iostream>
using namespace std;
void Fun(int n = 4)
{
	cout << n << endl;
}
void main()
{
	Fun();
	Fun(100);
	return 0;
}

缺省参数分为全缺省和半缺省参数。
全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值。C++规定半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值。

带缺省参数的函数调⽤,C++规定必须从左到右依次给实参,不能跳跃给实参。

#include<iostream>
using namespace std;
//全缺省
void Fun1(int a = 0, int b = 1, int c = 2)
{
	cout << "a=" << a << " ";
	cout << "b=" << b << " ";
	cout << "c=" << c << endl;
}
//半缺省
void Fun2(int a, int b, int c = 0)
{
	cout << "a=" << a << " ";
	cout << "b=" << b << " ";
	cout << "c=" << c << endl;
}
int main()
{
	Fun1();
	Fun1(6, 6, 6);
	Fun2(7, 7);
	Fun2(7, 7, 7);
	return 0;
}

 

并且在函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须在函数声明的时候给缺省值。

如我们在实现栈的时候需要用到Stack.h头文件进行函数的声明,这时候我们的缺省值就要在头文件的函数声明里给缺省值

有了缺省值,我们在实现栈的初始化时候可以不传这个指定的参数,那么该函数及默认使用缺省值开辟一块空间,若我们传入了对应的参数,那么栈就可以开辟一块指定大小的空间,这样就可以很灵活的控制栈的空间开辟,减少动态扩容时的消耗。

// Stack.h
#include <iostream>
#include <assert.h>
using namespace std;
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
void STInit(ST* ps, int n = 4);
#include"Stack.h"
// 缺省参数不能声明和定义同时给
void STInit(ST* ps, int n)
{
assert(ps && n > 0);
ps->a = (STDataType*)malloc(n * sizeof(STDataType));
ps->top = 0;
ps->capacity = n;
}
// test.cpp
#include"Stack.h"
int main()
{
ST s1;
STInit(&s1);
ST s2;
STInit(&s2, 1000);
return 0;
}

4. 函数重载

c++支持同一作用域出现同名的函数,但是要求这些同名函数的形参不同,可以是参数个数不同,也可以是参数类型不同。这样使得c++函数调用表现出多态行为,使用更灵活。而我们的c语言则是不支持的。

4.1参数类型不同

int Add(int left, int right)
{
    cout << "int Add(int left, int right)" << endl;
    return left + right;
} 
double Add(double left, double right)
{
    cout << "double Add(double left, double right)" << endl;
    return left + right;
}

4.2参数个数不同 

void f()
{
    cout << "f()" << endl;
} 
void f(int a)
{
    cout << "f(int a)" << endl;
} 

4.3 参数顺序不同

当然其本质还是参数类型不同 


void f(int a, char b)
{
    cout << "f(int a,char b)" << endl;
} 
void f(char b, int a)
{
    cout << "f(char b, int a)" << endl;
}

4.4 注意事项 

返回值不同不能作为重载的条件,因为在调用的时候可能无法区分,如以下两个函数

void fxx()
{
    return 0;
}

int fxx()
{
    return 0;
}

语法上构成重载,但是存歧义,编译器不知道调用谁,会报错

两个函数构成重载,因为第一个函数没有参数,第二个函数有一个参数是缺省值。然而第二个函数使用了缺省值,那么若我们在调用 f1() 时候他不知道到底要不要使用缺省值,因此会报错。

void f1()
{
    cout << "f()" << endl;
} 

void f1(int a = 10)
{
    cout << "f(int a)" << endl;
}

若两个函数的参数类型,个数,顺序都相同,只有参数名字不同,那么这两个函数就不是重载,他们运行起来也会报错,因为编译器无法识别这两个函数

void f(int a int b)
{
    cout<<"f(int a int b)"<<endl;
}

void f(int b,int a)
{
    cout<<"f(int b,int a)"<<endl;
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值