선언과 정의의 차이점

JellyPower·2023년 4월 15일
0

나만 몰랐던 C++

목록 보기
3/11
post-thumbnail
  • C/C++을 접해보면 가장 헷갈리는 것중 하나가 바로 선언정의가 분리돼있다는 것이다.
    • (비교적)모던한 객체지향 언어들인 Java, C#같은 애들은 선언 자체가 곧 정의이고 모듈시스템이 알아서 어떤 클래스, 함수가 있는지를 추적해주기때문에 선언과 정의를 구분할 필요가 없다. 그렇기에 선언과 정의에 관련된 내용은 더 헷갈리는거 같다.
  • 이번에는 C, C++에서 선언과 정의의 차이점이 무엇인지 알아보도록 하자.
💡 선언과 정의의 차이점: 선언과 정의의 가장 큰 차이는 "메모리를 할당하는가" 이다.

💡 더 정확히 말하면 "해당 오브젝트의 내부 구현체의 형태를 알고 있어서 메모리할당이 얼마나 일어날지를 알 수 있는가" 이다.

선언

  • 컴파일러가 참조할 식별자(identifier)와 이름을 알리는 것이 목적(어떤 번수, 함수가 있음을 알림)
  • 전방선언과 같이 무엇이 있다는 것을 알리는 작업들이 전부 선언임
  • extern 변수, static 같은 몇몇 타입들을 제외한 평범한 변수들의 경우 선언만 하고 정의를 하지 않는 것은 불가하다. 변수 자체가 메모리 공간을 요구하기 때문
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "Person.generated.h"

class UCard;// 전방선언

UCLASS()
class UNREALCOMPOSITION_API UPerson : public UObject
// 어떠한 클래스가 있다는 "선언" + 클래스의 구조를 "정의"
{
	GENERATED_BODY()
	
public:
	UPerson();
	// "선언", 실제 내부 함수의 내용이 어떤지 설명하지 않기에 메모리 레이아웃을 알 수 없음

	FORCEINLINE const FString& GetName() { return Name; }
	// "선언" + "정의", 함수가 있다는 것을 알림과 동시에 그 내용이 어떤지 설명함
	FORCEINLINE void SetName(const FString& InName) { Name = InName; }

	FORCEINLINE class UCard* GetCard() const { return Card; }
	FORCEINLINE void SetCard(class UCard* InCard) { Card = InCard; }

protected:
	UPROPERTY()
	FString Name;

	UPROPERTY()
	TObjectPtr<class UCard> Card;
	// 정의된 템플릿 클래스에 타입을 넣어주는 경우, 
	// 위와같이 템플릿 타입을 전방선언으로 넣어줄 수도 있음
};

정의

  • 타입, 인스턴스, 함수 같은 요소를 유일하게 설명하는 것이 목적
    • 유일하지 않고 똑같은 선언부에 대해 여러 정의를 가지면 컴파일러가 링커 에러를 던짐
    • 선언만되고 정의가 없으면 링커 에러를 던짐
  • 실제 정의된 함수는 코드, 데이터 영역에서 사용 될 수 있게 메모리에 할당됨
  • 실제로 할당된다기보단 메모리에 할당될 수 있게 메모리 레이아웃이 만들어짐
#include "Person.h"
#include "Card.h"

UPerson::UPerson()
{
	Name = TEXT("홍길동");
	Card = CreateDefaultSubobject<UCard>(TEXT("NAME_Card"));
}

구체적인 예시

C++ 에서 정의와 선언의 차이점

(참고: Large-Scale C++ Software Design)

  • 아래와 같은 것이 없으면 선언자체가 정의가 된다. 다르게 말하면, 아래의 선언들은 정의가 될 수 없다.
    • 구현체가 없는 함수 선언
    • extern 을 포함하고 있지만 초기자나 함수 구현체가 없는 것
    • 클래스 정의 내에 있는 정적(static) 멤버의 선언
      • 모든 클래스가 공유하기에 생성자 등에서 초기화 되는 것이 아니라 전역 스크립트에서 초기화 해줘야 하기 때문에 cpp파일에서 별도의 정의가 필요함.
    • 클래스 이름 선언
    • typedef 선언
  • 아래와 같은 것이 없으면 정의자체가 선언이 된다. 다르게 말하면, 아래의 정의들은 선언이 될 수 없다.
    • 정적인 클래스 데이터 멤버 정의
    • 인라인이 아닌 함수의 정의
profile
게임엔진코드싸개(진)

0개의 댓글