[C++] 클래스와 객체 1부

목차

클래스 및 개체의 파트 1:

                            1. 프로세스 지향과 객체 지향에 대한 기초적인 이해

                            2. 수업 소개

                            3. 클래스 정의

                            4. 클래스 액세스 한정자와 캡슐화

                            5. 클래스 범위

                            6. 클래스 인스턴스화

                            7. 클래스 객체의 크기 계산

                            8. 클래스 멤버 함수의 this 포인터


클래스 및 개체의 파트 1:

1. 프로세스 지향과 객체 지향에 대한 기초적인 이해

C 언어는 프로세스 지향적이며, 프로세스에 집중하고, 문제를 해결하기 위한 단계를 분석하고, 함수 호출을 통해 문제를 단계별로 해결합니다.

C++는 객체지향을 기반으로 객체에 집중하고, 한 가지를 다른 객체로 나누고, 객체 간의 상호 작용에 의존하여 완성합니다.

  

2. 수업 소개

C 언어 구조에서는 변수만 정의할 수 있는데, C++에서는 구조에 변수만 정의할 수 있는 것이 아니라 함수도 정의할 수 있다. 는 C++로 구현되며 구조체에서도 함수를 정의할 수 있음을 알 수 있습니다.

typedef int DataType;
struct Stack
{
     void Init(size_t capacity)
     {
         _array = (DataType*)malloc(sizeof(DataType) * capacity);
         if (nullptr == _array)
         {
             perror("malloc申请空间失败");
             return;
         }
         _capacity = capacity;
         _size = 0;
     }
    void Push(const DataType& data)
    {
         // 扩容
         _array[_size] = data;
         ++_size;
    }
     DataType Top()
     {
         return _array[_size - 1];
     }
     void Destroy()
     {
         if (_array)
         {
             free(_array);
             _array = nullptr;
             _capacity = 0;
             _size = 0;
         }
     }
     DataType* _array;
     size_t _capacity;
     size_t _size;
};
int main()
{
     Stack s;
     s.Init(10);
     s.Push(1);
     s.Push(2);
     s.Push(3);
     cout << s.Top() << endl;
     s.Destroy();
     return 0;
}

위 구조의 정의는 C++에서 대신 클래스를 사용하는 것이 좋습니다.

3. 클래스 정의

class className
{
    // 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号

class는 클래스를 정의하는 키워드, className은 클래스의 이름, {}는 클래스의 본문 클래스 정의 끝 뒤의 세미콜론은 생략할 수 없습니다.

클래스 본문의 내용을 클래스의 멤버라고 합니다. 클래스의 변수는 클래스의 속성 또는 멤버 변수라고 하고 클래스의 함수는 클래스의 메서드 또는 멤버 함수라고 합니다.

클래스를 정의하는 방법에는 두 가지가 있습니다.

1. 선언과 정의는 모두 클래스 본문에 위치 참고: 멤버 함수가 클래스에 정의된 경우 컴파일러는 이를 인라인 함수로 처리할 수 있습니다. 따라서 일반적으로 작고 자주 호출되는 함수는 클래스에 배치하고 나머지는 .cpp 파일에 배치하여 선언과 정의를 분리합니다.

2. 클래스 선언은 .h 파일에 있고 멤버 함수 정의는 .cpp 파일에 있습니다.멤버 함수 이름은 클래스 이름과 함께 추가되어야 합니다.

 일반적으로 두 번째 접근 방식이 더 바람직합니다. 선언과 정의를 분리한 이유: 다른 사람이 코드를 읽기에 편리하고 두 번째로 정적 라이브러리를 가져올 때 소스 코드 유출을 방지합니다. 선언이 정의에서 분리되면 정의는 클래스 도메인(void Stack::Init(int N = 4))을 반영해야 하며 이러한 클래스의 멤버도 클래스를 지정하기 때문에 마음대로 사용할 수 있습니다.

멤버 함수 명명 규칙에 대한 제안:

class Date
{
public:
     void Init(int year)
     {
         _year = year;
     }
private:
     int _year;
};
// 或者这样
class Date
{
public:
     void Init(int year)
     {
         mYear = year;
     }
private:
     int mYear;
};
// 其他方式也可以的,主要看公司要求。一般都是加个前缀或者后缀标识区分就行

4. 클래스 액세스 한정자와 캡슐화

액세스 한정자:

캡슐화를 달성하는 C++의 방법: 클래스를 사용하여 개체의 속성과 메서드를 결합하여 개체를 더 완전하게 만들고 액세스 권한을 통해 외부 사용자에게 선택적으로 인터페이스를 제공합니다.

 액세스 한정자 설명:

1. public으로 수정된 멤버는 클래스 외부에서 직접 액세스할 수 있습니다.

2. 보호 및 전용 수정 멤버는 클래스 외부에서 직접 액세스할 수 없습니다.

3. 접근 권한의 범위는 접근 한정자가 나타나는 위치부터 다음 접근 한정자가 나타날 때까지이다.

4. 뒤에 액세스 한정자가 없으면 범위는 }, 즉 클래스의 끝으로 이동합니다.

5. 클래스의 기본 액세스 권한은 비공개이고 구조체는 공개입니다(구조체가 C와 호환되기 때문)

참고: 1. 액세스 한정자는 컴파일 시에만 유용하며 데이터가 메모리에 매핑될 때 액세스 한정자 간에 차이가 없습니다.

           2. 액세스 한정자는 클래스 내부가 아닌 클래스 외부의 액세스를 제한합니다.

           3. C++은 컴파일러가 클래스를 전체적으로 다루기 때문에 위에서 정의된 변수나 함수가 필요하지 않습니다.

인터뷰 질문들:

질문: C++에서 구조체와 클래스의 차이점은 무엇입니까?

답변: C++은 C 언어와 호환되어야 하므로 C++의 구조체를 구조체로 사용할 수 있습니다. 또한 C++의 구조체를 사용하여 클래스를 정의할 수도 있습니다. 클래스 정의 클래스와 같지만, 차이점은 struct로 정의된 클래스의 기본 접근 권한은 public이고 class로 정의된 클래스의 기본 접근 권한은 private입니다. 참고: 상속 및 템플릿 매개변수 목록 위치에서 구조체와 클래스 간에도 차이가 있습니다.

캡슐화

인터뷰 질문들:

객체 지향의 세 가지 주요 특징: 캡슐화, 상속 및 다형성.

클래스 및 객체 단계에서는 주로 클래스의 캡슐화 특성을 연구합니다.

캡슐화: 데이터와 데이터 조작 방법을 유기적으로 결합하고 개체의 속성 및 구현 세부 정보를 숨기고 개체와 상호 작용할 수 있는 인터페이스만 노출합니다.

캡슐화는 본질적으로 사용자가 클래스를 더 쉽게 사용할 수 있도록 하는 관리입니다. 예: 컴퓨터와 같은 복잡한 장치의 경우 사용자에게 제공되는 유일한 것은 전원 버튼, 키보드 입력, 모니터, USB 잭 등을 통해 사용자가 컴퓨터와 상호 작용하고 일상 업무 등을 완료할 수 있도록 하는 것입니다. .,하지만 실제로 컴퓨터는 작동합니다.. CPU, 그래픽 카드, 메모리 등과 같은 일부 하드웨어 구성 요소입니다.

  

컴퓨터 사용자의 경우 마더보드의 회로가 어떻게 배치되어 있는지, 내부에 CPU가 어떻게 설계되어 있는지 등 내부 핵심 구성 요소에 대해 신경 쓸 필요가 없습니다. 키보드와 마우스를 통해 컴퓨터와 상호 작용합니다. 따라서 컴퓨터 제조업체가 공장을 떠날 때 내부 구현 세부 사항을 숨기기 위해 외부에 쉘을 놓고 사용자가 컴퓨터와 상호 작용할 수 있도록 스위치, 마우스 및 키보드 잭만 외부에 제공합니다.

C++ 언어로 캡슐화를 이루기 위해서는 데이터와 데이터를 운용하는 방법을 클래스를 통해 유기적으로 결합할 수 있고, 접근 권한을 통해 객체의 내부 구현 내용을 숨길 수 있으며, 제어 방법을 클래스 외부에서 직접 사용할 수 있다.

5. 클래스 범위

클래스는 새로운 범위를 정의하고 클래스의 모든 구성원은 클래스의 범위에 있습니다. 클래스 외부에서 멤버를 정의할 때 :: 범위 연산자를 사용하여 멤버가 속한 클래스 도메인을 나타내야 합니다.

class Person
{
public:
     void PrintPersonInfo();
private:
     char _name[20];
     char _gender[3];
     int  _age;
};
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{
     cout << _name << " "<< _gender << " " << _age << endl;
}

참고: 클래스는 구조체와 동일하며, 그 안에 타입이 정의되어 있고, 선언만 되어 있으며, 객체가 정의(st.a)되어 있어야만 접근이 가능합니다. 그러나 이 포인터 (Stack::Print)에 개체 주소가 전달되지 않기 때문에 클래스의 멤버 함수에 액세스하는 데 사용할 수 없습니다 .

6. 클래스 인스턴스화

클래스에서 객체를 생성하는 과정을 클래스의 인스턴스화라고 합니다.

1. 클래스는 객체에 대한 설명으로, 클래스의 구성원을 제한하지만 저장할 실제 메모리 공간을 할당하지 않고 클래스를 정의하는 모델과 같은 것입니다.

2. 클래스는 여러 개체를 인스턴스화할 수 있으며 인스턴스화된 개체는 실제 물리적 공간을 점유하고 클래스 멤버 변수를 저장합니다. Person 클래스에는 공간이 없으며 Person 클래스에서 인스턴스화된 객체만 특정 공간을 갖습니다.

int main()
{
     Person._age = 100;   // 编译失败:error C2059: 语法错误:“.”
     return 0;
}
3. 비유하자면 클래스에서 객체를 인스턴스화하는 것은 실제로 건축 설계 도면을 사용하여 집을 짓는 것과 같습니다. 디자인일 뿐이며 인스턴스화된 개체는 실제 데이터를 저장하고 물리적 공간을 차지할 수 있습니다.

 

7. 클래스 객체의 크기 계산

클래스 개체의 크기를 계산하는 방법:

class A
{
public:
    void PrintA()
    {
       cout<<_a<<endl;
    }
private:
    char _a;
};

질문: 클래스는 멤버 변수와 멤버 함수를 모두 가질 수 있으므로 클래스 개체에는 무엇이 포함되며 클래스 크기는 어떻게 계산합니까?

클래스 개체가 저장되는 방식을 추측합니다.

1. 객체 스토리지에는 클래스의 다양한 멤버가 포함됩니다.

결점: 각 객체의 멤버 변수는 다르지만 같은 함수가 호출되는데 이렇게 저장하면 하나의 클래스 저장소에 여러 객체를 생성할 때 각 객체가 코드의 복사본을 저장하게 되고 같은 코드가 생성된다. 여러번 저장했다 공간낭비, 어떻게 해결할까?
2. 코드 사본은 1개만 저장되며, 코드가 저장된 주소는 오브젝트에 저장됩니다.

 3. 멤버 변수만 저장하고 멤버 함수는 공개 코드 세그먼트에 저장

결론: 1. 실제로 위의 세 가지 저장 방법에 대해 컴퓨터는 세 번째 저장 방법에 따라 저장합니다.

           2. 클래스의 크기는 실제로 클래스의 멤버 변수의 합이므로 메모리 정렬에 주의해야 함은 물론이다.

           3. 빈 클래스의 크기는 1바이트입니다. 공간을 차지하고 유효한 데이터를 저장하지 않으며 객체가 존재함을 나타냅니다.

// 类中既有成员变量,又有成员函数
class A1 {
public:
    void f1()
    {}
private:
    int _a;
};
// 类中仅有成员函数
class A2 {
public:
   void f2() 
   {}
};
// 类中什么都没有---空类
class A3
{};

인터뷰 질문들

1. 구조를 정렬하는 방법은 무엇입니까? 메모리 정렬이 필요한 이유는 무엇입니까?

2. 지정된 정렬 매개변수에 따라 구조물을 정렬하는 방법은 무엇입니까? 3, 4, 5, 즉 임의의 바이트 수에 따라 정렬할 수 있습니까?

3. 빅 엔디안과 스몰 엔디안이란? 기계가 빅 엔디안인지 리틀 엔디안인지 테스트하는 방법 빅 엔디안인지 스몰 엔디안인지 고려해야 하는 시나리오를 본 적이 있습니까?

구조 메모리 정렬 규칙:

1. 첫 번째 구성원은 구조에서 오프셋 0 의 주소 에 있습니다.
2. 다른 멤버 변수는 특정 숫자(정렬 번호)의 정수배인 주소에 정렬되어야 합니다.
참고: Alignment = 컴파일러의 기본 정렬이며 멤버 크기의 작은 값입니다.
VS 의 기본 정렬 번호는 8 입니다.
3. 구조의 전체 크기는 다음과 같습니다. 최대 정렬 수의 정수배(가장 큰 변수 유형과 기본 정렬 매개변수가 가장 작음).
4. 구조가 중첩된 경우 중첩된 구조는 자체 최대 정렬의 정수배로 정렬되고 전체 구조는
본문 크기는 모든 최대 정렬(중첩 구조 포함)의 정수배입니다.

8. 클래스 멤버 함수의 this 포인터

이 포인터의 소개:

class Date
{ 
public:
     void Init(int year, int month, int day)
     {
     _year = year;
     _month = month;
     _day = day;
     }
     void Print()
     {
         cout <<_year<< "-" <<_month << "-"<< _day <<endl;
     }
private:
     int _year;     // 年
     int _month;    // 月
     int _day;      // 日
};
int main()
{
     Date d1, d2;
     d1.Init(2022,1,11);
     d2.Init(2022, 1, 12);
     d1.Print();
     d2.Print();
     return 0;
}

위의 클래스에는 다음과 같은 문제가 있습니다.

Date 클래스에는 두 개의 멤버 함수 Init와 Print가 있고, 함수 본문에 서로 다른 개체 간의 구분이 없으므로 di가 Init 함수를 호출할 때 d2 대신 di 개체를 설정해야 함을 함수가 어떻게 알 수 있습니까? 물체?

C++에서 이 문제는 this 포인터를 도입함으로써 해결됩니다. 즉, C++ 컴파일러는 각 "비정적 멤버 함수"에 숨겨진 포인터 매개변수를 추가하여 포인터가 현재 객체(함수를 호출하는 객체)를 가리키도록 합니다. 함수가 실행 중일 때), 함수 본문의 모든 "멤버 변수" 작업은 이 포인터를 통해 액세스되지만 모든 작업은 사용자에게 투명합니다. 즉, 사용자가 전달할 필요가 없으며 컴파일러가 자동으로 완료합니다. .

참고: 이 포인터 의 정의 및 전송 은 컴파일러의 작업이며 간섭할 수는 없지만 클래스에서 이 포인터를 사용할 수 있습니다.

이 포인터의 특징:
1. 이 포인터의 유형: 클래스 * const의 유형, 즉 멤버 함수에서 이 포인터에는 값을 할당할 수 없습니다.
2. 멤버 함수 내에서만 사용 가능
3. this 포인터는 기본적으로 멤버 함수의 형식 매개변수이며 개체가 멤버 함수를 호출할 때 개체 주소가 실제 매개 변수로 this 형식 매개 변수로 전달됩니다. 따라서 this 포인터는 개체에 저장되지 않습니다.
4. this 포인터는 멤버 함수의 첫 번째 암시적 포인터 매개변수로 일반적으로 ecx 레지스터를 통해 컴파일러에 의해 자동으로 전송되며 사용자가 전달할 필요가 없습니다.

 인터뷰 질문들:

1. 이 포인터는 어디에 있습니까?

this 포인터는 형식 매개변수로 사용되며 일반적으로 스택 프레임에 존재합니다. (VS는 통과하기 위해 ecx 레지스터를 사용하여 아래에서 최적화됨)

2. 이 포인터가 비어 있을 수 있습니까?

이 포인터는 비워둘 수 없으며 역참조 시 오류가 보고됩니다.

// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
     void Print()
     {
         cout << "Print()" << endl;
     }
private:
     int _a;
};
int main()
{
     A* p = nullptr;
     p->Print();
     return 0;
}
答案:C
//不发生过解引用,因为成员函数的地址不在对象中,在公共代码区域 Print(p) -> void Print(A* const this),传递一个指针不会报错
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{ 
public:
    void PrintA() 
   {
        cout << _a << endl;
   }
private:
     int _a;
};
int main()
{
    A* p = nullptr;
    p->PrintA();
    return 0;
}
答案:B
//cout<<this->_a 空指针解引用 程序运行崩溃 成员变量在对象里面,要通过this指针去访问

C 언어와 C++의 스택 비교:

1. C 언어 구현:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int DataType;
typedef struct Stack
{
	DataType* array;
	int capacity;
	int size;
}Stack;
void StackInit(Stack* ps)
{
	assert(ps);
	ps->array = (DataType*)malloc(sizeof(DataType) * 3);
	if (NULL == ps->array)
	{
		assert(0);
		return;
	}
		ps->capacity = 3;
	ps->size = 0;
}
void StackDestroy(Stack* ps)
{
	assert(ps);
	if (ps->array)
	{
		free(ps->array);
		ps->array = NULL;
		ps->capacity = 0;
		ps->size = 0;
	}
}
void CheckCapacity(Stack* ps)
{
	if (ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity * 2;
		DataType* temp = (DataType*)realloc(ps->array,
			newcapacity * sizeof(DataType));
		if (temp == NULL)
		{
			perror("realloc申请空间失败!!!");
			return;
		}
		ps->array = temp;
		ps->capacity = newcapacity;
	}
}
void StackPush(Stack* ps, DataType data)
{
	assert(ps);
	CheckCapacity(ps);
	ps->array[ps->size] = data;
	ps->size++;
}
int StackEmpty(Stack* ps)
{
	assert(ps);
	return 0 == ps->size;
}
void StackPop(Stack* ps)
{
	if (StackEmpty(ps))
		return;
	ps->size--;
}
DataType StackTop(Stack* ps)
{
	assert(!StackEmpty(ps));
	return ps->array[ps->size - 1];	
}
int StackSize(Stack* ps)
{
	assert(ps);
	return ps->size;
}

C언어로 구현했을 때 스택 관련 연산 함수는 다음과 같은 공통점을 가짐을 알 수 있다.

1. 각 함수의 첫 번째 매개변수는 Stack*

2. 매개변수가 NULL일 수 있으므로 함수에서 첫 번째 매개변수를 감지해야 합니다.

3. 함수에서 스택은 Stack* 매개변수를 통해 조작됩니다.

4. 스택 구조 변수의 주소는 호출 시 전달되어야 합니다.

참고: 데이터를 저장하는 구조만 구조에 정의할 수 있으며 데이터를 조작하는 방법은 구조에 넣을 수 없습니다. 즉, 데이터와 데이터를 조작하는 방법이 분리되어 구현이 상당히 복잡합니다. 많은 수의 포인터 작업 주의하지 않으면 실수할 수 있습니다.

2. C++ 구현

typedef int DataType;
class Stack
{
public:
	void Init()
	{
		_array = (DataType*)malloc(sizeof(DataType) * 3);
		if (NULL == _array)
		{
			perror("malloc申请空间失败!!!");
			return;
		}
		_capacity = 3;
		_size = 0;
	}
	void Push(DataType data)
	{
		CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	void Pop()
	{
		if (Empty())
			return;
		_size--;
	}
	DataType Top() { return _array[_size - 1]; }
	int Empty() { return 0 == _size; }
	int Size() { return _size; }
	void Destroy()
	{
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	void CheckCapacity()
	{
		if (_size == _capacity)
		{
			int newcapacity = _capacity * 2;
			DataType* temp = (DataType*)realloc(_array, newcapacity *
				sizeof(DataType));
			if (temp == NULL)
			{
				perror("realloc申请空间失败!!!");
				return;
			}
			_array = temp;
			_capacity = newcapacity;
		}
	}
private:
	DataType* _array;
	int _capacity;
	int _size;
};
int main()
{
	Stack s;
	s.Init();
	s.Push(1);
	s.Push(2);
	s.Push(3);
	s.Push(4);

	printf("%d\n", s.Top());
	printf("%d\n", s.Size());
	s.Pop();
	s.Pop();
	printf("%d\n", s.Top());
	printf("%d\n", s.Size());
	s.Destroy();
	return 0;
}

C++에서는 클래스를 통해 데이터와 데이터를 조작하는 메서드를 완벽하게 결합할 수 있으며, 액세스 권한을 통해 클래스 외부에서 호출할 수 있는 메서드, 즉 캡슐화를 제어할 수 있습니다. 사물을 인식하고 각 메서드는 Stack* 매개변수를 전달할 필요가 없으며 매개변수는 컴파일러가 컴파일된 후 자동으로 복원됩니다. 즉, C++의 Stack* 매개변수는 컴파일러에 의해 유지 관리됩니다. , C 언어는 사용자가 유지 관리해야 합니다.

추천

출처blog.csdn.net/qq_66767938/article/details/129935855