c++基础(1)

166 阅读4分钟

命名空间

命名冲突(为什么使用命名空间)

情景一:

有时我们定义了一个全局变量/函数/结构体类型,

然而库里也恰好定义了相同名称的全局变量/函数/结构体类型,

就会出现 重定义 或 分不清是变量、函数还是结构体类型 的问题.

#include <string.h>
#include <stdio.h>
int strcpy = 0;
int main()
{
	printf("%d",strcpy);
	return 0;
}

image.png

情景二:

一个项目在多组协作的情况下,也会出现名字冲突的问题.

image.png

4个小组都定义了相同名称的结构体类型,而且内部成员可能还不同.

于是c++便引入了命名空间.

命名空间的使用

定义命名空间

namespace yh //yh是命名空间的名称,可以自己取名
{
    //里面可以定义变量、定义函数、定义类型
    int variable;
	
    void function()
    {
	printf("hello world\n");
    }

    struct ListNode
    {
	int data;
	struct ListNode* next;
    };
    
}

访问命名空间的内容

1 指定命名空间访问.

:: 域作用限定符 —— 表示要去符号左边的域中找符号右边的内容

以上面定义的命名空间为例

int main()
{
	//去yh这个命名空间域中找到variable这个全局变量
	printf("%d\n", yh::variable);
	
	//去yh这个命名空间域中找到function()这个函数
	yh::function();

	//特殊:命名空间域要放到struct后面
	struct yh::ListNode node;
        
	node.data = 0;
	node.next = NULL;
	
        printf("%d    %p", node.data, node.next);
	return 0;
}

image.png

2 直接将整个命名空间展开

using namespace yh

此时不需要指定命名空间域去找对应的成员.

相当于不仅会去全局域中找,还会自动到已展开的命名空间域中找.

image.png

然而,若展开命名空间后会有命名冲突的风险.

image.png

展开命名空间域不代表这个命名空间域就成了全局域,仍然可以通过指定域访问避免命名冲突

image.png

那还不如不展开.....

3 展开某个命名空间中常用的变量/函数/类型

using yh::function;
using yh::ListNode; //这里就不能有struct
int main()
{
	//去yh这个域中找到variable这个全局变量
	printf("%d\n", yh::variable);
	function();


	struct ListNode node;
	node.data = 0;
	node.next = NULL;
	printf("%d    %p", node.data, node.next);
	return 0;
}

image.png

特殊

1 命名空间可以嵌套.

namespace yh
{
    int variable;
    
    namespace ll
    {
	void print()
	{
            printf("hello world\n");
	}
    }
}
int main()
{
    //访问variable
    yh::variable;

    //访问print()
    yh::ll::print();
    
    return 0;
}

image.png

2 在同一工程中(不同文件)允许存在多个相同名称的命名空间

编译器最后会把它们合到同一个命名空间中.

同一工程中:

如果在多个文件中或同一文件定义了多个相同名称的命名空间,编译器会把它们合在一起.

#include "test.h"
#include "test2.h"
//test.cpp
namespace yh
{	
    namespace ll
    {
	void print()
        {
            printf("hello world\n");
	}
    }
}
int main()
{
	//访问print()
	yh::ll::print();

	//访问function1()
	yh::function1();
	
	//访问function2()
	yh::function2();
	return 0;
}
//test.h
#pragma once
#include <stdio.h>
namespace yh
{
	void function1()
	{
		printf("hello world1\n");
	}
}
//test2.cpp
#include "test2.h"
void yh::function2()
{
	printf("hello world2\n");
}
//test2.h
#pragma once
#include <stdio.h>
namespace yh
{
    struct TreeNode
    {
	int data;
	struct TreeNode* left;
	struct TreeNode* right;
    };

    //这里只有声明
    void function2();
}

image.png

3 std命名空间

std是c++标准库的命名空间.

c++担心别人写的内容和库里的内容出现命名冲突,

把标准库中所有的内容都放到了std这个命名空间中.

所以,要使用标准库中的内容,

不仅要包含对应的头文件,还要指定是std这个命名空间.

image.png

域的补充

1 对于变量和类型,默认先在局部域找,再到全局域找; 对于函数,默认在全局域找.

image.png

一般不会把类型声明写到函数里面

image.png

2 同一个域中不能定义同名的变量、结构体,不同域中可以定义同名的变量、结构体

c++输入/输出

c语言的printf和scanf继续可以使用,但它们都要指定输入输出的类型;

c++中可以使用cin进行输入,cout进行输出,可以自动识别类型.

#include <iostream>//cin和cout的头文件
using namespace std;
int main()
{
	int val1;
	double val2;
	//cin 和cout可以自动识别类型
	cin >> val1 >> val2;
	cout << "hello " << val1 <<endl;//endl就相当于'\n'
	cout << val2 << endl;
	return 0;
}

image.png

1 cin和cout中的c其实就是console控制台,也就是运行程序时的黑框.

2 >> 叫做流提取运算符,cin >> val1 >> val2 就是提取控制台的输入到val1和val2中.

3 << 叫做流插入运算符,cout << val2 << endl就是把val2和换行的内容插入到控制台中.

缺省参数

什么是缺省参数

在声明或定义函数时,给函数的参数指定一个默认值(缺省值)

image.png

此时调用该函数:

如果没有指定参数,那参数就会采用该缺省值.
如果指定了参数,那缺省值不起作用.

image.png

这种参数就叫缺省参数.

怎么使用缺省参数

全缺省

函数的所有参数,全部给缺省值.

void test(int a = 1, int b = 2, int c = 3)
{
	cout << a << b << c << endl;
}
int main()
{
	test();
	test(4);
	test(4, 5);
	test(4, 5, 6);
	return 0;
}

注意:实参只能从左到右连续传,不能有间隔!!!

错误示范:

test( ,2, );

test(1, ,5);

test( , , 5);

半缺省

半缺省是指:缺省部分参数

void test1(int a, int b = 2, int c = 3)
{
	cout << a << b << c << endl;
}

void test2(int a, int b, int c = 4)
{
	cout << a << b << c << endl;
}

注意:形参只能从右往左连续缺省,不能有间隔!!!

此时,test1至少要传一个实参,test2至少要传两个实参.

用法举例

栈的初始化StackInit().

c语言中,栈的初始化一般是:

StackInit(struct Stack* ps)

里面的容量初始值是固定的.

例:#define INIT_CAPACITY 5

如果此时我同时需要两个栈:

A栈提前知道要插入100个数据.

B栈只需插入5个数据或者不清楚B栈要插入多少个数据.

那就有4种方案:

1 把初始容量扩大到100,此时的B栈会有一定程度的空间浪费.

2 初始容量不变,A栈需要慢慢扩容,扩容有一定销耗.

3 初始化多增加一个形参capacity,要求使用该函数时一定还要传个初始容量.

StackInit(struct Stack* ps, int capacity);

4 初始化多增加一个缺省参数capacity,视情况考虑是否需要传初始容量.

StackInit(struct Stack* ps, int capacity = 4);

若函数的声明和定义在同一文件中:

重要前提:当前文件的头文件展开以后

情况1:在当前文件,函数声明和定义同时出现缺省参数

无论参数的缺省值是否相同,无论声明和定义中的缺省参数数目是否相同.

都会报重定义默认的对应参数.

//test.h
#pragma once
#include <iostream>
void func(int a, int b, int c = 3);
//test.cpp
#include "test.h"//展开后相当于声明和定义在同一文件中
void func(int a = 1, int b = 2, int c = 3)
{
    std::cout << a << b << c<< std::endl; 
}
int main()
{
    func();
}

image.png

情况2:在当前文件 只有声明有缺省参数 或 只有定义有缺省参数.

函数的传参以有缺省参数的为准.

//test.h
#pragma once
#include <iostream>
void func(int a, int b, int c);
//test.cpp
#include "test.h"
using namespace std;
void func(int a = 3, int b = 2, int c = 1)
{
    cout << a << b << c << endl;
}
int main()
{
    func();
}

image.png

声明给缺省,定义不给缺省就可以按声明传参.

若函数的声明和定义在不同文件中:

重要前提:当前文件的头文件展开以后

情况1:函数的声明和定义同时出现缺省参数.

不报错,该函数传参以当前文件为准:

如果当前文件有声明无定义,以声明为准.

如果当前文件无声明有定义,以定义为准.

为演示该情况,将使用两个.cpp文件.

//test.h
#pragma once
#include <iostream>
void func(int a, int b = 6, int c = 6);
void print();
//test2.cpp
#include <iostream>
void func(int a = 3, int b = 2, int c = 1)
{
	std::cout << a << b << c << std::endl;
}

void print()
{
    //当前文件只有func的定义,所以会打印321
    func();
}
//test.cpp
#include "test.h"//相当于该文件只有func和print的声明
using namespace std;
int main()
{
    func(1);//以声明为准
	
    print();
    return 0;
}

image.png

image.png

情况2:在当前文件 只有声明有缺省参数 或 只有定义有缺省参数.

该函数传参以当前文件为准:

如果当前文件有声明无定义,以声明为准.

如果当前文件无声明有定义,以定义为准.

//test.h
#pragma once
#include <iostream>
void func(int a, int b, int c);
void print();
//test2.cpp
#include <iostream>
void func(int a = 1, int b = 2, int c = 3)
{
    std::cout << a << b << c << std::endl;
}

void print()
{
    //当前文件只有定义,所以会打印123
    func();
}
//test.cpp
#include "test.h"//相当于只有func的声明
using namespace std;
int main()
{
	func(9, 9, 6);//以声明为准
	
	print();
	return 0;
}

image.png

image.png

缺省参数放在声明还是定义?

原因:

由于定义只有一份,不可能同时出现在多个文件;

而声明可以在多个文件中出现.

结果:

建议把缺省参数写到函数的声明中,而函数定义不写缺省参数.

缺省值必须是全局变量或常量.