반응형
Notice
Recent Posts
Recent Comments
Link
«   2025/07   »
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
Tags
more
Archives
Today
Total
관리 메뉴

개발꿈나무

[C# 교과서] 17. C# 활용(11) - 생성자, 소멸자 본문

C# 기초

[C# 교과서] 17. C# 활용(11) - 생성자, 소멸자

HYOKYE0NG 2022. 1. 20. 08:04
반응형
생성자

 

생성자

 

  • 생성자는 단어 그대로 개체를 생성하면서 무엇인가를 하고자 할 때 사용되는 메서드로 개체를 초기화(주로 클래스 내 필드를 초기화)하는 데 사용된다.
  • 생성자는 생성자 이름이 클래스 이름과 동일하다는 규칙이 있다.
  • 생성자는 매개변수가 없는 기본(default) 생성자가 있고, 매개변수를 원하는 만큼 정의해서 사용할 수 있는데 이 때 반환값은 가지지 않는다.
  • 생성자도 static 생성자(정적 생성자)와 public 생성자(인스턴스 생성자)로 구분되며, 일반적으로 인스턴스 생성자를 많이 사용한다.
  • 모든 클래스는 적어도 생성자 하나를 가지는데, 사용하지 않는 기본 생성자는 코드에서 생략 가능하다.
  • 클래스 내에서 생성자를 자동으로 만들어주는 코드 조각(단축키)는 ctor을 입력한 수 Tab을 두 번 누르면 된다.
using System;

// 클래스
class ConstructorDemo
{
    // 생성자
    public ConstructorDemo()
    {
        Console.WriteLine("생성자가 호출되었습니다.");
    }

    // 진입점
    static void Main()
    {
        ConstructorDemo c = new ConstructorDemo();
    }
}

Car 클래스의 Car() 생성자는 Car 클래스의 인스턴스가 만들어질 때 자동으로 호출해서 실행된다.

 

using System;

namespace ClassAndInstance
{
    //[1] Dog 클래스
    public class Dog
    {
        //[2] name 필드
        private string name;

        //[3] name 매개변수를 받아서 name 필드에 저장하는 생성자
        public Dog(string name)
        {
            this.name = name; // 넘겨온 name을 name 필드에 임시 저장
        }

        //[4] name 필드의 값을 출력하는 반환값이 있는 메서드
        public string Cry()
        {
            return name + "이(가) 멍멍멍";
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            //[?] 인스턴스/개체/객체는 실체, 구체적인 것 정의
            //[5] Dog 클래스 사용:  happy, worry => 인스턴스, 개체, ...
            Dog happy = new Dog("해피");
            Console.WriteLine(happy.Cry());
            Dog worry = new Dog("워리");
            Console.WriteLine(worry.Cry());
        }        
    }
}

일반적으로 매개변수가 있는 생성자는 클래스 내에 선언된 특정 필드 값을 초기화하는 목적으로 사용된다.

생성자의 매개변수로 name을 선언했고 이 값을 사용하여 Dog 클래스의 name 필드를 초기화한다.

 

 

클래스에 생성자 여러 개 만들기

클래스에는 매개변수를 달리하여 생성자를 여러 개 만들 수 있는데 이러한 기능을 생성자 오버로드(constructor overload)라고 한다.

using System;

class ConstructorLog
{
    public ConstructorLog()
    {
        Console.WriteLine("기본 생성자 실행");
    }

    public ConstructorLog(string message)
    {
        Console.WriteLine("오버로드된 생성자 실행 : " + message);
    }
}

class ConstructorOverload
{
    static void Main()
    {
        ConstructorLog log1 = new ConstructorLog();
        ConstructorLog log2 = new ConstructorLog("C#");
        ConstructorLog log3 = new ConstructorLog("ASP.NET");
    }
}

 

 

정적 생성자와 인스턴스 생성자

클래스의 정적 멤버를 호출할 때 맨 먼저 호출되는 정적 생성자는 static 키워드로 만들며, 인스턴스 생성자는 public 키워드로 만든다.

using System;

namespace ConstructorAll
{
    public class Person
    {
        private static readonly string _Name;
        private int _Age;

        //[1] 정적 생성자
        static Person() { _Name = "홍길동"; }
        //[2] 인스턴스 생성자: 매개변수가 없는 생성자
        public Person() { _Age = 21; }
        //[3] 인스턴스 생성자: 매개변수가 있는 생성자
        public Person(int _Age)
        {
            this._Age = _Age; // this.필드 = 매개변수;
        }

        //[4] 정적 메서드
        public static void Show()
        {
            Console.WriteLine("이름 : {0}", _Name);
        }
        //[5] 인스턴스 메서드
        public void Print()
        {
            Console.WriteLine("나이 : {0}", _Age);
        }
    }
    class ConstructorAll
    {
        static void Main()
        {
            //[A] 정적 생성자 실행
            Person.Show(); // 정적인 멤버 호출

            //[B] 인스턴스 생성자 실행
            (new Person()).Print(); // 인스턴스 멤버 호출
            (new Person(22)).Print();
        }
    }
}

/*
이름: 백승수
나이: 21
나이: 22
*/

[1] static이 붙는 생성자는 정적 생성자로 [A]처럼 정적인 멤버가 호출될 때 먼저 실행된다.

[2], [3] 인스턴스 생성자를 선언했고, [B]처럼 인스턴스 멤버가 호출될 때 실행된다.

 

 

this() 생성자로 또 다른 생성자 호출하기

생성자에서 this()는 자신의 또 다른 생성자를 의미하며 this() 생성자로 매개변수가 있는 생성자에서 매개변수가 없는 생성자를 호출하거나 또 다른 생성자들을 호출할 수 있다.

using System;

namespace ConstructorThis
{
    class Say
    {
        private string message = "[1] 안녕하세요.";
        public Say() => Console.WriteLine(this.message);
        //[1] this() 생성자로 나 자신의 매개변수가 없는 생성자 먼저 호출 
        public Say(string message) : this()
        {
            //[2] 매개변수가 있는 생성자 자체도 호출 
            this.message = message;
            Console.WriteLine(this.message);
        }
    }

    class ConstructorThis
    {
        //[A] 매개변수가 있는 생성자 호출할 때 매개변수가 없는 생성자도 함께 호출
        static void Main() => new Say("[2] 잘가요.");
    }
}

/*
[1] 안녕하세요.
[2] 잘가요.
*/

[1] 매개면수가 있는 생성자 뒤에 콜론(:) 기호와 this()를 사용하여 자신의 매개변수가 없는 생성자를 먼저 호출하고 다시 매개변수가 있는 생성자를 호출한다.

 

 

생성자 포워딩

this() 생성자를 사용하면 생성자를 포워딩(forwarding)할 수 있으므로 다른 생성자에 값을 전달하기 좋다.

using System;

namespace ConstructorForwarding
{
    class Money
    {
        public Money() : this(1000) { } // 아래 생성자로 전송
        public Money(int money) => Console.WriteLine("Money: {0:#,###}", money);
    }

    class ConstructorForwarding
    {
        static void Main()
        {
            var basic = new Money(); // 1000
            var bonus = new Money(2000); // 2000
        }
    }
}

생성자 뒤에 오는 this()는 자신의 도 다른 생성자를 의미하며,

이러한 형태로 다른 생성자를 사용하여 값을 전달할 수 있다.

 

 

생성자를 사용하여 읽기 전용 필드 초기화

필드를 정의할 때 readonly 키워드를 붙일 수 있는데 이렇게 만들어진 필드를 읽기 전용 필드라고 한다.

읽기 전용 필드는 클래스의 생성자로만 초기화가 가능하고, 생성자로 초기화한 후에는 값을 변경할 수 없다.

using System;

public class WhitchService
{
    // 읽기 전용 필드
    private readonly string _serviceName;

    public WhitchService(string serviceName)
    {
        // 읽기 전용 필드는 생성자에 의해서 초기화해서 사용 가능
        _serviceName = serviceName;
    }

    public void Run() => Console.WriteLine($"{_serviceName} 기능을 실행합니다.");
}

class ReadOnlyNote
{
    static void Main()
    {
        var file = new WhitchService("[1] 파일 로그");
        file.Run(); //[1] 파일 로그 기능을 실행합니다.

        var db = new WhitchService("[2] DB 로그");
        db.Run(); //[2] DB 로그 기능을 실행합니다.
    }
}

읽기 전용은 선언과 동시에 초기화도 가능하고 선언한 후 생성자로 초기화할 수도 있다.

 

 

식 본문 생성자

화살표 연산자를 사용하여 함수를 줄여 표현하는 것처럼 생성자 코드를 줄여 표현할 수 있는데 이것을 식 본문 멤버 중이서 식 본문 생성자(expression bodied constructor)라고 한다.

using System;

namespace ExpressionBodiedConstructor
{
    class Pet
    {
        private string _name;

        // Expression Bodied Constructor
        public Pet(string name) => _name = name;

        public override string ToString()
        {
            return _name;
        }
    }

    class ExpressionBodiedConstructor
    {
        static void Main()
        {
            var pet = new Pet("야옹이");
            Console.WriteLine(pet.ToString());
        }
    }
}

 

 

 

 

소멸자

 

종료자

종료자(finalizer)라고도 하는 소멸자(destructor)는 닷넷의 가비지 수집기(Garbage Collector, GC)에서 클래스의 인스턴스를 사용한 후 최종 정리할 때 실행되는 클래스에서 가장 늦게 호출되는 메서드이다.

C#에서는 닷넷 가비지 수집기(GC)가 개체를 소멸할 때 메모리를 해제하는 등 역할을 대신해 주기 때문에 소멸자에 직접 접근할 일이 없다.

Class Car
{
	~Car()
    {
    	//개체가 소멸될 때 필요한 기능 수행
    }
}

 

가비지 수집기

C#에서 메모리 관리는 닷넷에 내장된 GC라는 가비지 수집기가 관리한다.

특정 클래스의 인스턴스를 생성한 후 해당 인스턴스를 제거하는 코드를 따로 사용하지 않아도 되는 것이 GC 엔진이 하는 역할이다.

 

 

생성자, 메서드, 소멸자 실행 시점 살펴보기

using static System.Console;

public class DestructorTest
{
    // 생성자
    public DestructorTest()
    {
        WriteLine("[1] 생성");
    }
    // 메서드
    public void Run()
    {
        WriteLine("[2] 실행");
    }
    // 소멸자: GC
    ~DestructorTest()
    {
        WriteLine("[3] 소멸");
    }
}

class ConstructorToDestructor
{
    static void Main()
    {
        DestructorTest test = new DestructorTest(); // 생성
        test.Run(); // 실행
        // GC.Collect(); // 소멸
    }
}

/*
[1] 생성
[2] 실행
[3] 소멸
*/

 

소멸자를 사용한 클래스 역할 마무리하기

using System;

namespace DestructorDemo
{
    public class Car
    {
        private string _name; // 필드
        public string GetName() // 메서드
        {
            return _name;
        }
        public Car() // 생성자(매개 변수가 없는)
        {
            _name = "승용차";
        }
        public Car(string name) // 생성자(매개 변수가 있는)
        {
            this._name = name;
        }
        //[!] 소멸자 : GC가 호출
        ~Car() // 소멸자
        {
            Console.WriteLine("{0} 폐차...", _name);
        }
    }

    class DestructorDemo
    {
        static void Main(string[] args)
        {
            Car car1 = new Car();
            Console.WriteLine(car1.GetName());
            Car car2 = new Car("캠핑카");
            Console.WriteLine(car2.GetName());
        }
    }
}

/*
승용차
캠핑카
캠핑카 폐차...
승용차 폐차...
*/

가비지 수집기는 Stack 구조로 데이터를 처리하기 때문에 LIFO 형식으로 구현된다.

 

 

 

 

 

<Reference>

 

반응형
Comments