Compartilhamento de tecnologia

Primeira introdução ao c (namespace, parâmetros padrão, sobrecarga de funções)

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

1. Espaço para nome

1. O significado do espaço para nome

Em C/C++, há um grande número de variáveis, funções e classes a serem aprendidas posteriormente. Os nomes dessas variáveis, funções e classes existirão em todo o mundo.

No âmbito local, pode causar muitos conflitos.O objetivo do uso de namespaces é localizar os nomes dos identificadores para evitar nomenclatura

Conflito ou poluição de nomes, a palavra-chave namepac parece resolver esse problema.

Conflitos de nomenclatura como o programa a seguir em projetos de linguagem C são problemas comuns que o C++ introduz o namepac para melhor resolvê-los.

Tal problema: (O erro é relatado porque a variável rand que definimos entra em conflito com a função rand em stdlib.h)

#include <stdio.h>
#include <stdlib.h>
int rand = 10;
int main()
{
	// 编译报错:error C2365: “rand”: 重定义;以前的定义是“函数”
	printf("%dn", rand);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2. Definição de namespace

1. Para definir um namespace, você precisa usar a palavra-chave namespace, seguida pelo nome do namespace e, em seguida, conectar um par de {}, onde {}

Ou seja, um membro do namespace. Variáveis/funções/tipos, etc. podem ser definidos no namespace.(Observe que {} não é seguido por ";")

2. A essência do namespace é definir um domínio. Este domínio é independente do domínio global. Diferentes domínios podem definir variáveis ​​​​com o mesmo nome.

O rand acima não está mais em conflito.

#include<iostream>
#include <stdio.h>
#include <stdlib.h>
namespace tmp
{
	int rand = 0;
}
int main()
{
	printf("%dn", rand);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

3. Os domínios em C++ incluem domínio local de função, domínio global, domínio de namespace e domínio de classe. O domínio afeta a sintaxe de tempo de compilação para encontrar uma variável/função/;

A lógica do tipo origem (declaração ou definição), com isolamento de domínio, os conflitos de nomes são resolvidos.Além de afetar o domínio local e o domínio global

A compilação da lógica de pesquisa também afetará o ciclo de declaração de variáveis. Os domínios de namespace e os domínios de classe não afetam o ciclo de declaração de variáveis.

4. O namespace só pode ser definido globalmente e, claro, também pode ser aninhado.

#include<iostream>
#include <stdio.h>
#include <stdlib.h>
namespace tmp
{
	int rand = 0;
	namespace tmp1
	{
		int Add(int left, int right)
		{
			return left + right;
		}
		struct Node
		{
			struct Node* next;
			int val;
		};
	}

}
int main()
{
	printf("%dn", rand);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

5. Namespaces com o mesmo nome definidos em vários arquivos do projeto serão considerados como um namespace e não entrarão em conflito.

arquivo teste.c

#include<iostream>
#include <stdio.h>
#include <stdlib.h>
#include"test.h"
namespace tmp
{
	int rand = 0;
	namespace tmp1
	{
		int Add(int left, int right)
		{
			return left + right;
		}
		struct Node
		{
			struct Node* next;
			int val;
		};
	}

}
int main()
{
	printf("%dn", tmp::d);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

arquivo teste.h

#pragma once
namespace tmp
{
	int d = 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5

Pode-se descobrir que a variável d no espaço tmp pode ser usada no arquivo .c.

6. A biblioteca padrão C++ é colocada em um namespace chamado std (padrão).

7. Se quisermos usar variáveis ​​no domínio global, podemos fazer isto:

int a = 3;
int main()
{
	int a = 0;
	printf("%dn", ::a);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Desta forma, printf imprime 3 primeiro.

3. Uso de namespace

Ao compilar para buscar a declaração/definição de uma variável, por padrão ela buscará apenas local ou globalmente, não no namespace.então

O programa a seguir irá compilar e relatar um erro.

namespace tmp
{
	int d = 0;
	namespace tmp1
	{
		int Add(int left, int right)
		{
			return left + right;
		}
		struct Node
		{
			struct Node* next;
			int val;
		};
	}
}
int main()
{
	int a = 0;
	printf("%dn", d);//未定义标识符d
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

Portanto, temos que usar variáveis/funções definidas no namespace. Existem três maneiras:

1. Especifique o acesso ao namespace. Este método é recomendado no projeto.

tmp::d
  • 1

2. Usar expande um membro do namespace.Essa abordagem é recomendada para membros do projeto acessados ​​com frequência e que não têm conflitos.

namespace tmp
{
	int d = 0;
	namespace tmp1
	{
		int Add(int left, int right)
		{
			return left + right;
		}
		struct Node
		{
			struct Node* next;
			int val;
		};
	}
}
using tmp::d;
int main()
{
	int a = 0;
	printf("%dn", d);//未定义标识符d
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

3. Expanda todos os membros no namespace,O projeto não é recomendado porque o risco de conflito é alto. O programa de prática diária é recomendado por conveniência.

namespace tmp
{
	int d = 0;
	namespace tmp1
	{
		int Add(int left, int right)
		{
			return left + right;
		}
		struct Node
		{
			struct Node* next;
			int val;
		};
	}
}
using namespace tmp;
int main()
{
	int a = 0;
	printf("%dn", d);//未定义标识符d
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

2. Entrada e saída

1. É a abreviatura de Input Output Stream. É uma biblioteca de fluxo de entrada e saída padrão que define entrada e saída padrão.

fora do objeto.

2. std::cin é um objeto da classe istream, que é orientado principalmente para a saída padrão de caracteres estreitos (do tipo char).

Ingresso.

3. std::cout é um objeto da classe ostream, que é orientado principalmente para o fluxo de saída padrão de caracteres estreitos.

4. std::endl é uma função Quando o fluxo é inserido na saída, é equivalente a inserir um caractere de nova linha e atualizar o buffer.

5. &lt;&lt; é o operador de inserção de fluxo e &gt;&gt; é o operador de extração de fluxo. (A linguagem C também usa esses dois operadores para realizar operações bit a bit, como deslocamento para a esquerda/deslocamento para a direita)

6. É mais conveniente usar C++ para entrada e saída. Não há necessidade de especificar manualmente o formato como printf/scanf para entrada e saída C**++.

A saída pode identificar automaticamente tipos de variáveis(Essencialmente, isso é conseguido através da sobrecarga de funções). Na verdade, o mais importante é que os fluxos C++ possam suportar melhor a personalização.

Digite objetos de entrada e saída.

#include<iostream>
#include <stdio.h>
#include <stdlib.h>
namespace tmp
{
	int a = 0;
	double b = 0.0;
	char c = '0';
	namespace tmp1
	{
		int Add(int left, int right)
		{
			return left + right;
		}
		struct Node
		{
			struct Node* next;
			int val;
		};
	}
}
using namespace tmp;
using namespace std;
int main()
{
	cin >> a >> b >> c;
	cout << a << b << c;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

7. cout/cin/endl, etc. todos pertencem à biblioteca padrão C++. A biblioteca padrão C++ é colocada em um namespace chamado std (padrão), portanto, é necessário.

Use-os por meio do uso de namespace.

8、⼼Podemos usar namespace std na prática diária geral, mas não é recomendado usar namespace std no desenvolvimento real do projeto.

3. Parâmetros padrão

1. Definição e regulamentação de parâmetros padrão

Os parâmetros padrão especificam um valor padrão para os parâmetros da função ao declarar ou definir a função.Ao chamar esta função, se nenhum parâmetro real for especificado

Em seguida, o valor padrão do parâmetro formal é usado, caso contrário, o parâmetro real especificado é usado. Os parâmetros padrão são divididos em parâmetros padrão completos e semi-padrão. (Em alguns lugares,

Os parâmetros padrão também são chamados de parâmetros padrão).

Padrão completo significa que todos os parâmetros formais recebem valores padrão, e semi-padrão significa que alguns parâmetros formais recebem valores padrão.C++ estipula que os parâmetros semi-padrão devem ser da direita para a esquerda

Contínuo é padronizado em sequência e não pode pular para o valor padrão em intervalos.

Para chamadas de função com parâmetros padrão, o C++ estipula que os parâmetros reais devem ser fornecidos sequencialmente da esquerda para a direita e os parâmetros reais não podem ser ignorados.

Quando a declaração e a definição da função são separadas, os parâmetros padrão não podem aparecer tanto na declaração quanto na definição da função. É estipulado que a função deve ser declarada como padrão.

valor.

#include <iostream>
using namespace std;
void Func(int a = 0)
{
	cout << a << endl;
}
int main()
{
	Func(); // 没有传参时,使⽤参数的默认值
	Func(10); // 传参时,使⽤指定的实参
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

Insira a descrição da imagem aqui

2. Padrão total e semi-padrão

#include <iostream>
using namespace std;
// 全缺省
void Func1(int a = 10, int b = 20, int c = 30)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl << endl;
}
// 半缺省
void Func2(int a, int b = 10, int c = 20)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl << endl;
}
int main()
{
	Func1();
	Func1(1);
	Func1(1, 2);
	Func1(1, 2, 3);
	Func2(100);
	Func2(100, 200);
	Func2(100, 200, 300);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

O semi-padrão não pode ser escrito assim:

void Func2(int a = 10, int b, int c = 20)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl << endl;
}
//或者
void Func2(int a = 10, int b, int c)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl << endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

Deve cumprir rigorosamente:

Os parâmetros semi-padrão devem ser padronizados continuamente da direita para a esquerda e não podem ser ignorados para os valores padrão em intervalos.

3. Aplicação prática de parâmetros padrão

Antes de aprendermos C++, quando implementamos a inicialização e inserção de pilha, escrevemos isto:

typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;
	int capacity;
}ST;
// 栈顶
void STInit(ST* ps, int n)
{
	assert(ps);
	ps->a = (STDataType*)malloc(n * sizeof(STDataType));
	ps->top = 0;
	ps->capacity = n;
}
void STPush(ST* ps, STDataType x)
{
	assert(ps);
	// 满了, 扩容
	if (ps->top == ps->capacity)
	{
		printf("扩容n");
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity
			* 2;
		STDataType* tmp = (STDataType*)realloc(ps->a,
			newcapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		ps->a = tmp;
		ps->capacity = newcapacity;
	}
	ps->a[ps->top] = x;
	ps->top++;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

Se quisermos inserir 100 dados, precisaremos de expansão contínua e perda eficiente, mas depois de aprendermos os parâmetros padrão, podemos escrever assim:

// 栈顶
void STInit(ST* ps, int n = 4)
{
	assert(ps);
	ps->a = (STDataType*)malloc(n * sizeof(STDataType));
	ps->top = 0;
	ps->capacity = n;
}
void STPush(ST* ps, STDataType x)
{
	assert(ps);
	// 满了, 扩容
	if (ps->top == ps->capacity)
	{
		printf("扩容n");
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity
			* 2;
		STDataType* tmp = (STDataType*)realloc(ps->a,
			newcapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		ps->a = tmp;
		ps->capacity = newcapacity;
	}
	ps->a[ps->top] = x;
	ps->top++;
}
int main()
{
	ST a;
	STInit(&a, 100);//这里不传100也可以,因为规定必须从左到右依次给实参,不能跳跃给实参。刚好和缺省参数确定位置互补
	for (int i = 0; i < 100; i++)
	{
		STPush(&a, i);
	}
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

Isso evita efetivamente o problema de abrir espaço repetidamente.

4. Sobrecarga de funções

C++ suporta funções com o mesmo nome que aparecem no mesmo escopo, mas exige que os parâmetros formais dessas funções com o mesmo nome sejam diferentes.

Tipos diferentes. Dessa forma, as chamadas de função C++ exibem comportamento polimórfico e são mais flexíveis de usar. (No entanto, valores de retorno diferentes não podem ser usados ​​como condições de sobrecarga.

Como não pode ser distinguido durante a chamada, se o valor de retorno e o tipo ou número do parâmetro mudarem ao mesmo tempo, também é uma condição de sobrecarga).

1. Diferentes tipos de parâmetros

#include<iostream>
using namespace std;
// 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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2. O número de parâmetros é diferente

// 2、参数个数不同
void f()
{
	cout << "f()" << endl;
}
void f(int a)
{
	cout << "f(int a)" << endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Em particular, se o método acima usar parâmetros padrão, um erro será relatado ao chamar sem passar parâmetros. O compilador não sabe quem chamar.

// 下⾯两个函数构成重载
// f()但是调⽤时,会报错,存在歧义,编译器不知道调⽤谁
void f1()
{
	cout << "f()" << endl;
}
void f1(int a = 10)
{
	cout << "f(int a)" << endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3. A ordem dos parâmetros é diferente (na verdade, os tipos de parâmetros são diferentes)

// 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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
#include<iostream>
using namespace std;
// 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;
}
// 2、参数个数不同
void f()
{
	cout << "f()" << endl;
}
void f(int a)
{
	cout << "f(int a)" << endl;
}
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;
}
int main()
{
	Add(10, 20);
	Add(10.1, 20.2);
	f();
	f(10);
	f(10, 'a');
	f('a', 10);
	return 0;
}
`#include<iostream>
using namespace std;
// 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;
}
// 2、参数个数不同
void f()
{
	cout << "f()" << endl;
}
void f(int a)
{
	cout << "f(int a)" << endl;
}
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;
}
int main()
{
	Add(10, 20);
	Add(10.1, 20.2);
	f();
	f(10);
	f(10, 'a');
	f('a', 10);
	return 0;
}``

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81

resultado:
Insira a descrição da imagem aqui