Class
- 어떤 종류의 모든 객체에게 공통인 멤버 변수와 멤버 함수를 정의하는 형틀
- 객체 지향에서의 기본적인 빌딩 블록
- 인스턴스 : 클래스로부터 만들어지는 객체
- 멤버 변수 : 클래스 내부의 변수, 인스터스에 개별 존재
- 멤버 함수 : 클래스 내부의 함수, 인스턴스가 1개의 멤버함수 공유
멤버 변수 내부 정의 주의점
- 멤버 변수의 경우, 내부에 정의하게 될 경우, 인라인 함수로 사용 되기에 주의 해야 한다.
=default
기본 구현을 사용하여 생성자, 소멸자, 복사연산자 및 이동연사자를 명시적으로 정의할 때 사용
class MyClass { public: MyClass() = default; // 기본 생성자 ~MyClass() = default; // 소멸자 MyClass(const MyClass&) = default; // 복사 생성자 MyClass& operator=(const MyClass&) = default; // 복사 대입 연산자 MyClass(MyClass&&) = default; // 이동 생성자 MyClass& operator=(MyClass&&) = default; // 이동 대입 연산자 // 다른 멤버 함수들... };
new와 일반 선언 차이
특징 | new 사용 (동적 할당) |
변수 선언 (자동/정적 할당) |
---|---|---|
메모리 영역 | 힙 (Heap) | 스택 (Stack) 또는 정적 |
생명 주기 | 명시적으로 관리 (delete ) |
스코프 종료 시 자동 소멸 |
성능 | 메모리 할당/해제가 느림 | 상대적으로 빠름 |
유연성 | 더 큰 데이터 구조에 유리 | 간단하고 안전함 |
new
를 사용하는 경우: 객체를 스코프와 무관하게 더 오랫동안 유지하거나, 동적으로 크기 조정 가능한 데이터 구조(예: 동적 배열, 연결 리스트 등)를 사용할 때 적합.- 변수 선언을 사용하는 경우: 성능과 코드 안전성이 중요한 경우, 객체의 생명 주기가 스코프 내로 제한되어도 되는 경우 적합.
생성자
- 기본 생성자
Employee() {}
- 복사 생성자
Employee(const Employee& employee) {}
복사 생성자
- 동일한 클래스의 객체를 복사하여 객체를 생성할 때 사용
- 호출되는 경우
- 같은 종류의 객체로 초기화 :
MyClass obj(obj2);
- 정의 시에만 호출, 이미 생성된 객체 간에는 대입 연산
- 객체를 함수에 전달 :
MyClass func(MyClass obj) { // 여기서 호출 };
- 함수가 객체를 반환 :
MyClass func(MyClass obj) { MyClass tmp; return tmp;// 여기서 호출 };
- 같은 종류의 객체로 초기화 :
C++ 복사
- int, char 배열은 기본적으로 DeepCopy를 지원.
- 객체의 경우, 기본적으로 ShallowCopy를 지원.
- 기본 복사 생성자를 사용 시, 배열 복사에서 문제가 발생하는 이유.
- 대입 연산의 경우(같은 객체 간 대입), 깊은 복사
MyClass obj("Hello"); obj = obj; // 자기 자신에 대한 대입
초기화 리스트
C++에서 객체의 생성자(constructor)를 호출할 때 멤버 변수를 초기화하는 방법 중 하나
// 초기화 리스트를 사용하여 멤버 변수를 초기화 MyClass(int a, int b) : memberA(a), memberB(b) { std::cout << "Constructor called!"; }
생성자의 본문이 실행되기 전에 멤버 변수를 초기화하는 데 사용
본문에서 초기화 하는 것보다 효율적
int a = 10; // <- 초기화 리스트 방식 int a; a = 10; // <- 초기화 리스트x
상수 멤버 변수 초기화 : 상수 멤버 변수를 초기화 리스트로 초기화가능
C++11 부터 초기화 시,
{}
를 사용하는 uniform initialization으로 통일AClass a {1, 2, 3};
- 축소 변환 (3.14를 3으로 자르는 int 변환같은)을 방지하는 기능이 있음
C++20 부터는 지정 초기자 기능 사용가능
AClass{ int a {3}; int b; int c = 5; } AClass a { .a = 5; .b = 4; } // a = 5, b = 4, c = 5 로 초기화 // 생략된 대상은 디폴트 값으로 초기화; // 구조체에 멤버를 추가해도 기존 코드는 정상 작동
초기자 리스트 생성자
- 참고 : 전무가를 위한 c++
initializer_list<{type}>
을 매개변수로 가지는 생성자class ES { public: ES(initializer_list<double> args) { for (const auto& value : args) { m_sequence.push_back(value); } } }
위임 생성자
생성자에서 다른 생성자를 호출
위임한 생성자를 먼저 호출함
class Person { public: // 기본 생성자 Person() : Person("Unknown", 0) { std::cout << "Default constructor called\n"; } // 이름만 초기화하는 생성자 Person(const std::string& name) : Person(name, 0) { std::cout << "Constructor with name only called\n"; } // 이름과 나이를 초기화하는 생성자 (메인 생성자) Person(const std::string& name, int age) : name(name), age(age) { std::cout << "Main constructor called\n"; } }; int main() { Person p1; // 기본 생성자 호출 Person p2("Alice"); // 이름만 초기화 Person p3("Bob", 30); // 이름과 나이 초기화 p1.printInfo(); // Main -> Default p2.printInfo(); // Main -> Name Only p3.printInfo(); // Main }
명시적 생성자
생성자는 받은 값을 암묵적으로 변환하여 호출 됨. (변환 생성자)
이를 막고자 하면,
explicit
키워드를 생성자 앞에 붙임.- 암묵적 변환이 필요한 경우 아니면,
explicit
지정 권장
- 암묵적 변환이 필요한 경우 아니면,
사용 예
void process(const MyClass& c) class MyClass{ public: explicit MyClass(int); } process({1}) // X <- 암시적 변환 허용 막기 process(MyClass{1}); // O
사용자 정의 소멸자 사용 주의점
- c++11부터 사용자 정의 소멸자가 있으면, 기본으로 복제 생성자를 생성해주지 않음
복제 대입연산자
AClass& operator=(const AClass& rhs) = default
이동 연산자
- 이동 연산자를 사용하면, 복제 대신 얇은 복사와 비슷한 방식으로 소유권을 옮김.
- 소유권을 옮긴 것이기에 주소는 여전히 해당 위치를 가르키고 있음.
- 그렇기에, nullptr로 초기화 해 주는게 좋음
&&
를 사용해서 매개변수를rvalue
로 변환,noexcept
키워드를 사용해 중간에 에러로 멈추지 않도록 함- 함수에서 로컬 변수나 매개 변수를 리턴할 때는 사용 금지
- RVO, NRVO를 막아 최적화가 더 안좋아짐
- 3항 연산자로 return 문에 작성도 최적화를 막으니 사용 금지
- 이동 생성자
SpreadSheet::SpreadSheet(SpreadSheet&& src) noexcept {};
- 이동 대입 연산자
SpreadSheet& SpreadSheet::operator=(SpreadSheet&& src) noexcept {};
'공부 > C++' 카테고리의 다른 글
(C++17,20)구조적 바인딩 (0) | 2025.03.05 |
---|---|
튜플(Tuple) (0) | 2025.03.05 |
함수 (1) | 2025.03.03 |
enum_class (0) | 2025.03.03 |
C++ 변수 (0) | 2025.03.03 |