기술나눔

생성자, 정적 멤버, 친구, 내부 클래스의 초기화 목록 [클래스 및 객체(2부)]

2024-07-12

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

추신: 다음 코드는 VS2022 환경에서 테스트되었지만 모든 컴파일러가 이를 통과할 수 있다는 의미는 아닙니다.
PS: 어떤 테스트 코드도 헤더 파일 stdio.h의 선언을 표시하지 않습니다. 사용할 때 직접 추가하세요.

  

여기에 이미지 설명을 삽입하세요.

                                           블로거 홈페이지: LiUEEEEE
                                                C++ 열
                                              C 언어 칼럼
                                            데이터 구조 열
                                            정렬 알고리즘 열
                                         Niuke의 고전적인 주제에 관한 칼럼

1. 생성자의 초기화 목록

1.1 생성자를 되돌아보기


이전 블로그에서는

          생성, 소멸, 복사 [클래스 및 객체(2부)]
클래스의 생성자는 에서 언급됩니다. 날짜 클래스를 예로 들면 초기화 방법은 다음과 같습니다.

class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

위 코드에서 볼 수 있듯이 C++에 대해 더 자세히 공부하지 않으면 사용자는 일반적으로 생성자의 코드가 클래스 멤버 변수의 초기화라고 생각하지만 사실은 C++에서 생성자의 기본 문은 초기화만 합니다. 초기화는 한 번만 초기화할 수 있는 반면 생성자는 멤버 변수에 값을 여러 번 할당할 수 있으므로 할당은 진정한 의미에서 초기화라고 할 수 없습니다.

	Date(int year, int month, int day)
	{
		_year = year;
		_year += 1;
	}
  • 1
  • 2
  • 3
  • 4
  • 5

1.2 초기화 목록


이때 C++의 초기화 목록이 도입됩니다.
초기화 목록은 ":"(콜론)으로 시작하고 ","(쉼표)로 구분된 데이터 멤버 목록입니다. 각 멤버 변수 뒤에는 대괄호가 오고, 대괄호 안의 값은 초기화된 값입니다. :
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
  • 1
  • 2
  • 3
  • 4
  • 5

알아야 할 사항은 다음과 같습니다.

  1. 각 멤버 변수는 초기화 목록에 한 번만 나타날 수 있습니다. (초기화는 한 번만 초기화될 수 있습니다.)
  2. 클래스에는 초기화를 위해 초기화 목록에 배치되어야 하는 다음 멤버가 포함되어 있습니다.
    • 참조 멤버 변수
    • 사용자 정의 유형 멤버 변수(그리고 사용자 정의 유형에는 기본 생성자가 없습니다. 매개변수 없는 생성자, 완전 기본 생성자, 시스템에서 자동으로 생성되는 생성자)
    • const 수정된 멤버 변수

위에서 언급한 사용자 지정 멤버 변수(기본 생성자 없음)는 정의된 사용자 지정 멤버 변수의 생성자가 매개 변수가 있는 생성자이며 사용자가 생성자를 명시적으로 구현하는 경우 매개 변수를 초기화하기 전에 전달해야 함을 의미합니다. 초기화 목록이 작성되지 않으면 컴파일러가 사용자를 위해 자동으로 생성합니다. 그러나 사용자 정의 멤버 변수의 초기화에 매개변수 참여가 필요한 경우 시스템은 기본적으로 매개변수를 전달하지 않으므로 위의 예방 조치가 수행됩니다. 후속 사용 시 불필요한 문제가 발생하지 않도록 초기화가 취소되는 상황을 방지하는 것입니다.

1.3 초기화 목록에 대한 팁


초기화 목록을 작성할 때 컴파일러는 선언 순서에 따라 순차적으로 변수를 초기화하므로 멤버 변수의 초기화 순서는 클래스의 선언 순서와 일치해야 합니다. 다음과 같은 불필요한 문제:
class A
{
public:
	A(int n)
		:_a2(n)
		,_a1(_a2)
	{}
	void Print()
	{
		cout << "_a1 = " << _a1 << endl;
		cout << "_a2 = " << _a2 << endl;
	}
private:
	int _a1;
	int _a2;
};

int main()
{
	A aa(100);
	aa.Print();

	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

여기에 이미지 설명을 삽입하세요.
위 그림과 같이 초기화를 하게 되면 기대에 미치지 못하는 결과가 나오므로 사용 시에는 이러한 조작을 피해야 합니다.



2. 명시적 키워드


생성자에는 전용 함수도 있습니다.단일 매개변수 생성자또는여러 매개변수가 있지만 첫 번째 매개변수에 대한 기본값이 없는 생성자암시적 유형 변환. 예:
class Date
{
public:
	Date(int year)
		: _year(year)
	{}
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
class Date
{
public:
	Date(int year, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

예를 들어 값을 전달하여 할당하는 경우:

	Date date(2024);
  • 1

컴파일러 레벨에서는 2024를 생성된 클래스 객체에 직접 할당하지 않고 암시적 변환을 통해 임시 클래스 객체로 변환한 후, 이러한 객체를 통해 초기화해야 하는 클래스 객체에 2024를 할당한다. 초기화된 클래스 객체는 A이고 임시 클래스 객체는 B입니다.

	Date A(2024);
	等价于
	Date A = B//B的值为2024
  • 1
  • 2
  • 3

암시적 변환을 방지하기 위해 C++에서는 명시적 키워드를 생성자 앞에 추가하면 암시적 변환이 발생하는 것을 방지할 수 있습니다. 예를 들면 다음과 같습니다.
	explicit Date(int year)
		: _year(year)
	{}
  • 1
  • 2
  • 3




3. 정적 멤버

3.1 정적 멤버 개념


static으로 선언된 클래스 멤버를 클래스의 정적 멤버라고 합니다. static으로 수정된 멤버 변수를 static 멤버 변수라고 합니다. 정적 멤버 변수는 클래스 외부에서 초기화되어야 합니다. 예를 들면 다음과 같습니다.
class Date
{
public:
	Date(int year, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
	static int _i;
};

int Date::_i = 1;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

3.2 정적 멤버 특성


  1. 정적 멤버는 모든 클래스 개체에서 공유되며 특정 개체에 속하지 않습니다. 정적 영역에 저장됩니다.
  2. 정적 멤버 변수는 클래스 외부에서 정의해야 하며 정의 시 static 키워드는 추가되지 않습니다.
  3. 클래스 정적 멤버는 클래스 이름::정적 멤버 또는 object.static 멤버를 사용하여 액세스할 수 있습니다.
  4. 정적 멤버 함수에는 숨겨진 이 포인터가 없으며 비정적 멤버에 액세스할 수 없습니다.
  5. 정적 멤버는 클래스의 멤버이기도 하며 공개, 보호 및 비공개 액세스 한정자에 의해 제한됩니다.




4. 유위안


친구는 캡슐화를 돌파하는 방법을 제공하고 때로는 편리함을 제공합니다. 그러나 친구는 결합도를 높이고 캡슐화를 파괴하므로 친구를 두 번 이상 사용해서는 안 됩니다.

친구는 친구 기능과 친구 클래스로 구분됩니다.

4.1 친구 기능


Friend 함수는 클래스 외부의 함수가 클래스의 전용 멤버에 액세스해야 하는 경우 클래스에서 이 함수를 클래스의 friend 함수로 선언하여 액세스할 수 있음을 의미합니다. 예를 들면 다음과 같습니다.
class Date
{
	friend void Print(Date& d);
public:
	Date(int year = 1, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};
void Print(Date& d)
{
	cout << d._year << " " << d._month << " " << d._day <<endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • Friend 함수는 클래스의 private 및 protected 멤버에 액세스할 수 있지만 클래스의 멤버 함수에는 액세스할 수 없습니다.
  • 친구 함수는 const로 수정할 수 없습니다.
  • Friend 함수는 클래스 정의의 어느 곳에서나 선언될 수 있으며 클래스 액세스 한정자에 의해 제한되지 않습니다.
  • 함수는 여러 클래스의 친구 함수일 수 있습니다.
  • 친구 함수를 호출하는 원리는 일반 함수와 동일합니다.

4.2 친구 수업


클래스가 다른 클래스의 비공개 멤버에 액세스하려는 경우 액세스해야 하는 클래스에서 이 클래스를 친구로 선언한 다음 해당 비공개 멤버에 액세스할 수 있습니다. 예를 들면 다음과 같습니다.
class Time
{
	friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类
					   //中的私有成员变量
public:
	Time(int hour = 0, int minute = 0, int second = 0)
			: _hour(hour)
			, _minute(minute)
			, _second(second)
	{}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
			: _year(year)
			, _month(month)
			, _day(day)
	{}	
	void SetTimeOfDate(int hour, int minute, int second)
	{
		// 直接访问时间类私有的成员变量
		_t._hour = hour;
		_t._minute = minute;
		_t._second = second;
	}
private:
	int _year;
	int _month;
	int _day;
	Time _t;
};
  • 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
  • 친구 관계는 일방적이며 교환적이지 않습니다. 예를 들어 위의 Time 클래스와 Date 클래스에서 Date 클래스를 Time 클래스의 friend 클래스로 선언하면 Date 클래스에서 Time 클래스의 private 멤버 변수에 직접 액세스할 수 있지만 Time 클래스에 있는 Date 클래스의 전용 멤버 변수입니다.
  • C가 B의 친구이고 B가 A의 친구라면 C가 A의 친구라는 의미는 아닙니다.




5. 내부수업


C++에서는 내부 클래스라고 하는 클래스 내부에 다른 클래스를 생성하는 기능이 지원됩니다. 내부 클래스는 외부 클래스에 속하지 않으며 외부 클래스의 개체를 통해 내부 클래스의 멤버에 액세스할 수 없습니다. . 외부 클래스는 내부 클래스에 대한 우수한 액세스 권한을 갖지 않습니다.
그리고 내부 클래스는 당연히 외부 클래스의 친구입니다. 즉, 내부 클래스는 외부 클래스의 전용 멤버에 액세스할 수 있습니다.

특성:

  1. 내부 클래스는 외부 클래스에서 공개, 보호 또는 비공개로 정의될 수 있습니다.
  2. 내부 클래스는 외부 클래스의 개체/클래스 이름을 요구하지 않고 외부 클래스의 정적 멤버에 직접 액세스할 수 있습니다.
  3. sizeof(외부 클래스)=외부 클래스, 내부 클래스와는 아무런 관련이 없습니다.




6. 결론


여기에 이미지 설명을 삽입하세요.

  제 원본 글을 봐주셔서 정말 감사드립니다.
  이 기사는 주로 개인 학습 및 지식 공유를 위해 사용됩니다. 실수가 있으면 수정해 주셔서 감사합니다.
  인용이 필요하신 경우 주소를 기재해주세요.