생성자(Constructor)
// JAVA
public class Vector
{
private int x;
private int y;
// 기본 0으로 자동 초기화
// 매개변수 없는 생성자
// 안만들어도 상관없음
public Vector() // 위의 멤버 변수에 값을 지정하는 것과 같아서 필요성이 적음
{
x = 0;
y = 0;
}
}
// JAVA 는 라이브러리를 만들어서 배포하는 할 때, cpp 와 헤더 파일이 분리 되어있지 않음
// 패키지를 사용하는 사람들은 해당 소스코드를 볼 방법이 없음(publice, private 모두)
// 또한 제작자는 위에서 부터 데이터를 보면서 무슨 데이터가 있는지를 볼 수 있기 때문에
// private 를 우선적으로 명시하는 습관을 가졌을거라 유추
-------------------------------------------------------------
// C++
class Vector
{
public:
// 매개변수 없는 생성자
Vector()
{
mX = 0;
mY = 0;
}
private:
int mX;
int mY;
}
// C++ 는 라이브러리를 사용하는 사람에겐 private 의 접근권한이 없으니 의미가 없음
// 따라서 접근 가능한 public 을 우선적으로 명시하는 슴관을 가졌을거라 유추
C++ 의 초기화 리스트
// 대입
class Vector
{
public:
Vector()
{
mX = 0;
mY = 0;
}
private:
int mX;
int mY;
}
-----------------------------------------------
// 초기화리스트
class Vector
{
public:
Vector()
: mX(0)
, mY(0)
{
}
private:
int mX;
int mY;
}
: 시점에서 초기화리스트 영역으로 멤버 변수의 값을 초기화할 수 있음
멤버 변수를 대입없이 초기화를 하며, 상수나 참조 변수도 초기화가 가능함
생성자 본문에서의 대입연산 초기화에선 다음과 같은 차이를 볼 수 있다
class X
{
const int nNameSize;
AnotherClass& mAnother;
X(AnotherClass& another)
{
mNameSize = 20; // 에러
mAnother = another; // 에러
}
};
-------------------------------------------------------
class X
{
const int nNameSize;
AnotehrClass& mAnother;
X(AnotherClass& another)
: mNameSize(20)
, mAnother(another)
{
}
};
const 와 참조 변수는 초기화시 값을 정해줘야 되기 때문에 생성자 본문에서의 대입 연산을 적용되지 않는다.
여기서 위의 private 의 차선 명시와 함께 멤버 변수 초기화시 중요한 부분이 멤버 변수 선언이 후에 발생하는 만큼,
멤버 변수의 의존 관계가 있는 멤버 변수 끼리의 사용이나 초기화 측면에서 순서를 잘 지켜야함.
class BadInit {
public:
BadInit() : b(a), a(5) {}
private:
int a;
int b;
};
이 코드의 경우 b(a) 를 먼저 실행하려고 하지만, a 는 아직 초기화되지 않았기 때문에 미정의 동작이 발생 할 수 있음
그렇기 때문에 a(5) 를 먼저 초기화 시킨 후 b(a) 를 하겠끔, 멤버 변수가 명시된 순서대로 초기화를 적용시켜야 함
또한 다음 코드처럼
class A {
public:
A() {
x = 5; // 멤버 x가 선언되지 않았다면 오류
}
private:
int x;
};
초기화가 아닌 생성자 본문에서 대입을 통해 값을 전달하는 경우이기 때문에 이미 기본 생성자를 통해 초기화된 후 대입이 일어나는 비효울이 발생할 수 있음
여기서 조금 의문이 드는 게 초기화리스트도 어찌됐든 변수 선언과 별개의 코드로 값을 넣어주는 동작을 명시한건데 이게 어떻게 대입 연산과 다르다는 걸까? 하는 생각이 들 수 있음
우리는 보통 초기화란 작업을 '메모리 정의와 동시에 값을 넣어주는 행위' 로 인지하고 있기 때문에
그래서 대입연산(=) 도 i
nt a = 5 는 초기화
int a; a = 5 는 대입연산
이라고 염연히 구분을 짓기 때문에 더 혼란스러울 수 있음
이건 생성자 호출 시점을 기준으로 보면 명확하게 나줘져있음을 알 수 있음
객체는 생성시 생성자 호출 전에 객체의 레이아웃을 구성하면서
멤버 변수의 선언과 메모리 정의도 같이 진행됨
여기에 더해서 초기화리스트까지 작업까지 동시에 이뤄지고 난 후
생성자 본문이 실행되면서 코드의 실행부가 실행된다고 이해하면 된다.
조금 더 C++ 스러운 클래스 만들기
Cpp 에서 멤버 변수의 값을 0 으로 초기화 시켜주는게 C++98 에선 보편적으로 지향하는 설정이다
SetX() 같은 함수를 통해 멤버 변수의 값을 확실하게 설정 해준다면 굳이 설정하지 않아도 되지만,
그렇지 않다면 임의의 초기값을 넣어줌으로써, 정의되지 않은 값과 같은 에러로부터 안전하게 지킬 수 있다.
코드로 보는 C++
// Vector 클래스 Vector1.h
#pragma once
class Vector1
{
public:
Vector1();
Vector1(int x, int y);
private:
int mX;
int mY;
};
// Vector1.cpp
#include <iostream>
#include "Vector1.h"
using namespace std;
Vector1::Vector1()
: mX(0)
, mY(0)
{
cout << "Vector1(): (" << mX << mY ", " << mY << "0" << endl;
}
Vector1::Vector1(int x, int y)
: mX(x)
, mY(y)
{
cout << "Vector1(int x, int y): (" << mX << ", " << mY << ")" << endl;
}
기본생성자
- 매개변수를 받지 않음
- 클래스에 생성자가 없으면 컴파일러가 기본 생성자를 자동적으로 만들어줌
- 자동적으로 만들어진 생성자는
- 멤버 변수를 초기화하지 않음
- 반면에 모든 포함된 개체의 생성자를 호출
컴파일러가 하는 일
생성자가 하나도 없을 경우 기본 생성자를 생성
생성자가 존재하면 기본 생성자는 생성하지 않음
생성자 오버로딩
- 여러개의 생성자를 만들 수 있음
- 같은 이름
- 인자의 개수나 자료형은 다름
소멸자(Destructor)
// JAVA
public class Vector
{
private int x;
private int y;
// 소멸자 없음
}
---------------------------------------------------
// C++
// Vector.h
class Vector
{
public:
~Vector(); // 소멸자
private:
int mX;
int mY;
};
// Vector.cpp
Vector::~Vector() // 소멸자 정의
{
}
- 개체가 지워질 때 호출됨
- 가상 소멸자
- 스코프를 벗어날 때 자동으로 호출
- JAVA
- 자동으로 garbage 를 수집하기 때문에 소멸자 없음
- C++
- C++ 클래스는 그 안에서 동적으로 메모리를 할당할 수도 있음
- 그런 경우 필히 소멸자에서 메모리를 직접 해제해 줘야 함
클래스 안에서의 동적 메모리 할당 예시
// MyString.h
class MyString
{
public:
MyString();
private:
char* mChars;
int mLength;
int mCapacity;
};
----------------------------------------------------------------------
// MyString.cpp
MyString::MyString()
: mLength(0)
, mCapacity(15)
{
mChars = new char[mCapacity + 1];
}
-----------------------------------------------------------------------
// main.cpp
void Foo()
{
Mystring name;
// ...
}
객체의 생성과 해제 과정에서의 메모리 상태
객체가 사라지면 힙 영역 할당시 사용하던 메모리의 주소 정리가 되지 않는 상황이 발생
이와 같은 문제를 해결하기 위해서 소멸자가 존재
// MyString.h
class MyString
{
public:
MyString();
~MyString(); // 힙 메모리 등을 정리
private:
char* mChars;
int mLength;
int mCapacity;
};
----------------------------------------------------------------------
// MyString.cpp
MyString::MyString()
: mLength(0)
, mCapacity(15)
{
mChars = new char[mCapacity + 1];
}
MyString::~Mysrint()
{
delete[] mChars; // 소멸자 호출시 delete 동작
// 객체가 사라지기 때문에 초기화 할 필요가 없음
// mCapacity = 0;
// mChars = NULL;
}
'C++ > FOCU_C++' 카테고리의 다른 글
C++ chpt9. (4) | 2025.07.24 |
---|---|
C++ chpt8. (1) | 2025.07.21 |
C++ chpt6. (1) | 2025.05.16 |
C++ chpt5. (0) | 2025.05.04 |
C++ chpt4. (0) | 2025.05.03 |