[C++] 함수 오버로딩 및 참조

목차

1. 함수 오버로딩

 1. 함수 오버로딩의 개념

 2. 명의변경

둘, 인용

 1. 참고 개념

 2, 인용 특성

 3. 자주 인용

 4. 참조된 사용 시나리오

   4.1 매개변수 만들기

   4.2 반환 값 수행

 5. pass-by-value와 pass-by-reference 효율성 비교

 6. 참조와 포인터의 차이점


1. 함수 오버로딩

 1. 함수 오버로딩의 개념

함수 오버로딩: 함수의 특수한 경우입니다. C++에서는 유사한 함수를 가진 동일한 이름 의 여러 함수를 동일한 범위 에서 선언할 수 있습니다. 이러한 함수의 형식 매개변수 목록( 매개변수 수 또는 유형 또는 유형 순서 ) 은 이름 이 다릅니다 . 함수 구현을 처리하는 데 자주 사용되는 다른 데이터 유형의 문제와 유사합니다.

#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;
}
// 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;
}
int main()
{
	Add(10, 20);      //调用int Add(int left, int right)
	Add(10.1, 20.2);  //调用double Add(double left, double right)
 
	f();              //调用void f()
	f(10);            //调用void f(int a)

	f(10, 'a');       //调用void f(int a, char b)
	f('a', 10);       //调用void f(char b, int a)
	return 0;
}

 2. 명의변경

  C++이 함수 오버로딩을 지원하는 이유는 이름 수정에 있는데 왜 C++은 함수 오버로딩을 지원하고 C언어는 함수 오버로딩을 지원하지 않는 것일까?

C/C++에서 프로그램이 실행되려면 전처리, 컴파일, 어셈블링, 링크   단계를 거쳐야 합니다 .

컴파일 단계   에서 컴파일러는 각 함수 구현의 이름을 수정한 다음 함수가 호출될 때 수정된 이름을 일치시키고 일치하는 함수의 주소를 함수 호출에 제공하고 호출 명령을 사용하여 해당 함수 구현은 함수 구현에서 수행됩니다. C++에서 함수 오버로딩을 지원하고 C언어에서 함수 오버로딩을 지원하지 않는 이유는 함수 이름 데코레이션 규칙이 다르기 때문이다 . C언어에서는 수정 후 함수의 이름이 그대로 유지되므로 함수는 어떤 함수를 호출해야 할지 알 수 없지만, C++에서는 함수 수정 후 매개 변수 유형이 다르기 때문에 함수 이름이 다르게 나타나므로 매번 함수는 매개변수 유형에 따라 적절한 함수 구현을 호출합니다.

다음에서 gcc 함수의 이름이 수정 후에도 변경되지 않은 것을 확인할 수 있습니다. 그리고 g++의 수정된 함수는 [_Z+함수 길이+함수 이름+타입 이니셜]이 됩니다.

  • C언어 컴파일러로 컴파일한 결과

   결론: Linux에서는 gcc로 컴파일한 후 함수 이름 수정이 변경되지 않았습니다. 

  • C++ 컴파일러로 컴파일한 결과

   결론: Linux에서는 g++로 컴파일한 후 함수 이름이 수정되고 컴파일러는 수정된 이름에 함수 매개 변수 유형 정보를 추가합니다.

  •  Windows에서의 이름 수정 규칙

둘, 인용

 1. 참고 개념

참조는 변수의 새로운 정의가 아니라 기존 변수의 별칭 입니다 컴파일러는 참조 변수에 대한 메모리 공간을 열지 않으며 참조하는 변수와 동일한 메모리 공간을 공유합니다 . 사람들에게 별명을 부여하는 것과 같습니다.

 유형 및 참조 변수 이름(객체 이름) = 참조 엔터티;

void TestRef()
{
    int a = 10;
    int& ra = a;     //<====定义引用类型
    printf("%p\n", &a);
    printf("%p\n", &ra);
}

   그들의 주소는 동일하며 둘 다 메모리 공간을 공유한다는 것을 나타냅니다.

   참고: 참조 유형은항목동일한 유형이어야합니다.

 2, 인용 특성

  1. 참조는 정의될 때 초기화되어야 합니다.
  2. 변수는 여러 참조를 가질 수 있습니다.
  3. 참조가 엔터티를 참조하면 다른 엔터티를 참조할 수 없습니다.
void TestRef()
{
    int a = 10;
    // int& ra;   // 该条语句编译时会出错
    int& ra = a;
    int& rra = a;

    int b = 20;
    //int& ra = b;  // 该条语句编译时会出错

    printf("%p %p %p\n", &a, &ra, &rra);
}

  그들의 주소도 동일합니다.

 3. 자주 인용

void TestConstRef()
{
    const int a = 10;
    //int& ra = a;   // 该语句编译时会出错,a为常量
    const int& ra = a;

    // int& b = 10; // 该语句编译时会出错,b为常量
    const int& b = 10;

    double d = 12.34;
    //int& rd = d; // 该语句编译时会出错,类型不同
    const int& rd = d;
}

 4. 참조된 사용 시나리오

   4.1 매개변수 만들기

매개변수를 만드는 것은 매개변수를 복사하는 것과 동일하며 이 두 매개변수를 위한 공간을 다시 열 필요가 없습니다.

#include<iostream>
using namespace std;
void swap(int& left, int& right)
{
	int tmp = left;
	left = right;
	right = tmp;
}
int main()
{
	int a = 10, b = 20;
	cout << "交换前:" << a << " " << b << endl;
	swap(a, b);
	cout << "交换后:" << a << " " << b << endl;
	return 0;
}

   4.2 반환 값 수행

먼저 코드를 살펴보겠습니다.

#include<iostream>
using namespace std;
int& fun()
{
	int a = 10;
	return a;
}
int main()
{
	int& ret = fun();
	cout << ret << endl;
	return 0;
}

작업 결과:

위의 연산 결과를 보면 우리가 원하는 결과를 얻지만 이 상황은 우연의 일치이고 사실 그 안에 숨겨진 비밀이 있다.

#include<iostream>
using namespace std;
int& fun()
{
	int a = 10;
	return a;
}
int main()
{
	int& ret = fun();
    //在这多加一行代码
	cout << "hello world" << endl;
	cout << ret << endl;
	return 0;
}

 작업 결과:

코드를 한 줄 더 추가하면 원하는 결과가 나오지 않는 이유는 무엇입니까?

변수 a는 지역 변수이기 때문에 함수의 범위를 벗어나면 소멸됩니다. 소멸 전에 임시 참조 변수를 사용하여 a의 참조를 저장합니다(임시 변수가 tmp라고 가정(유형 int&)), tmp와 ret은 모두 변수 a의 별칭이고 a는 범위를 벗어나면 해제됩니다. 해당 코드를 추가하지 않았다면 출력 결과는 정확합니다. 공간이 해제됩니다.원하는 경우 한 줄의 코드를 추가하는 것은 스택 프레임을 다시 설정하는 것과 같습니다. 이때 a가 수정되었으므로 반환 값은 임의의 값이 됩니다.

위의 코드를 수정하는 것은 변수 a를 static 변수로 설정하여 static 영역에 저장하는 것이므로 괜찮습니다.

#include<iostream>
using namespace std;
int& fun()
{
	static int a = 10;
	return a;
}
int main()
{
	int& ret = fun();
	cout << "hello world" << endl;
	cout << ret << endl;
	return 0;
}

작업 결과:

참고: 함수가 함수의 범위를 벗어나 반환되는 경우 반환된 객체가 여전히 존재하는 경우(아직 시스템으로 반환되지 않음) 참조로 반환할 수 있으며 시스템으로 반환된 경우 반환해야 합니다. 값.

 5. pass-by-value와 pass-by-reference 효율성 비교

값은 매개변수나 반환값의 형태로 사용되는데, 매개변수 전달 및 반환 시 함수는 실제 매개변수를 직접 전달하거나 변수 자체를 직접 반환하는 것이 아니라 실제 매개변수를 전달하거나 변수의 임시 복사본을 반환하므로 값은 매개 변수로 사용되거나 반환 값 유형이 매우 비효율적이며 특히 매개 변수 또는 반환 값 유형이 매우 큰 경우 효율성이 더욱 떨어집니다.

 즉, 값에 의한 전달은 임시 사본을 생성하지만 참조에 의한 전달은 사용하지 않으므로 참조에 의한 전달의 효율성이 더 높아집니다.

 6. 참조와 포인터의 차이점

문법적 개념 에서 참조는 독립된 공백이 없고 참조되는 개체와 동일한 공백을 공유하는 별칭입니다. 기본 구현 에는 참조가 포인터 형식으로 구현되기 때문에 실제로 공간이 있습니다 .

int main()
{
	int a = 10;

	int& ra = a;
	ra = 20;

	int* pa = &a;
	*pa = 20;

	return 0;
}

참조 및 포인터의 어셈블리 코드를 살펴보겠습니다.

참조와 포인터의 차이점:

  1. 참조는 개념적으로 변수의 별칭을 정의하고 포인터는 변수 주소를 저장합니다.
  2. 참조는 정의될 때 초기화되어야 하며 포인터는 필요하지 않습니다.
  3. 참조가 초기화 중에 엔티티를 참조한 후에는 더 이상 다른 엔티티를 참조할 수 없으며 포인터는 언제든지 동일한 유형의 엔티티를 가리킬 수 있습니다.
  4. NULL 참조는 없지만 NULL 포인터는 있습니다.
  5. 의미는 sizeof에서 다릅니다. 참조 결과는 참조 유형의 크기 이지만 포인터는 항상 주소 공간이 차지하는 바이트 수 입니다 (32비트 플랫폼에서 4바이트).
  6. 자체 참조 추가는 참조된 엔터티가 1씩 증가함을 의미하고, 포인터 자체 증가는 포인터가 유형의 크기를 역방향으로 오프셋한다는 것을 의미합니다.
  7. 다단계 포인터는 있지만 다단계 참조는 없습니다 .
  8. 엔터티에 액세스하는 방법에는 여러 가지가 있습니다. 포인터는 명시적으로 역참조되어야 하며 참조 컴파일러에서 자체적으로 처리합니다 .
  9. 참조는 포인터보다 사용하기에 상대적으로 안전합니다 .

 

 


이 기사에 부족한 점이 있으면 아래에 의견을 보내 주시면 최대한 빨리 수정하겠습니다.

​​​​​​​

  오래된 아이언, 좋아하고 주목하는 것을 잊지 마십시오!!!   

추천

출처blog.csdn.net/m0_63198468/article/details/131218115