[C#] 클래스, 생성자, 종료자

2023. 8. 12. 19:09공부/C#

 

클래스 개념 참고: https://zheldajdajd.tistory.com/9

 

[C++] 클래스(class)와 객체(instance)

클래스(class)와 객체(instance) 목차 1. 절차적 프로그래밍&객체지향 프로그래밍 2. 클래스와 객체(instance) 1. 절차적 프로그래밍 & 객체지향 프로그래밍 절차적 프로그래밍 객체지향 프로그래밍 - 프

zheldajdajd.tistory.com

 

C#에서 클래스를 생성하는 방법은 다음과 같다.

 

class 클래스 이름
{
        //데이터와 메소드
}

 

class Menu
{
   //필드
    public string Name;
    public int Price;
 
   //메소드
    public void Order()
    {
    	Console.WriteLine("{0}을 주문했습니다.",Name);
    }
}

 

클래스에 선언된 변수들은 필드라고 한다. 

또한 필드와 메소드, 프로퍼티, 이벤트 등 클래스에 선언된 요소들은 '멤버' 라고 한다.

 

이제 Menu의 객체를 만들어보자.

 

Menu pizza=new Menu();
pizza.Name="피자";
pizza.Price=20000;
pizza.Order();

 

 

Menu pizza=new Menu()의 Menu()는 생성자다.

생성자는 클래스의 이름과 동일하며 객체를 생성하는 역할을 한다.

그 앞에 붙은 new 키워드는 생성자를 호출하여 객체를 생성하는 역할을 한다.

 

만약 new 키워드로 생성자를 호출하지 않는다면 어떤 일이 일어날까?

 

pizza는 그 자체에 메모리가 할당되는 것이 아닌  참조로 객체가 있는 곳을 가리킨다.

따라서 객체를 생성하지 않는다면 pizza가 가리킬 객체는 존재하지 않기 때문에 null 값을 가지게 된다.

때문에 new 연산자를 이용하여 생성자를 호출해야 한다.

 

 

이제 예제 코드를 살펴보자.

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Csharp_Project
{
    class Menu
    {
        public string Name;
        public int Price;

        public void Order()
        {
            Console.WriteLine("{0}를 주문했습니다.", Name);            
        }
    }

    class Class_Ex
    {
        static void Main(string[] args)
        {
            Menu pizza = new Menu();
            pizza.Name = "피자";
            pizza.Price = 20000;
            pizza.Order();
            Console.WriteLine("{0}의 가격은 {1}원 입니다.", pizza.Name, pizza.Price);

            Menu coke = new Menu();
            coke.Name = "콜라";
            coke.Price = 1500;
            coke.Order();
            Console.WriteLine("{0}의 가격은 {1}원 입니다.", coke.Name, coke.Price);
        }     
    }
}

 

 

<실행 결과>

 

 

 

 


 

생성자도 우리가 원한다면 구현이 가능하다.

 

class 클래스 이름
{
        한정자 클래스이름(매개변수)
        {
           //
        }
        //필드
        //메소드
{

 

하지만 명시적으로 생성자를 구현하지 않아도 컴파일러에서는 자동으로 생성자를 만들어준다. 

이 때 만들어진 생성자를 '기본 생성자' 라고 한다.

 

그렇다면 생성자를 구현해야 하는 이유는 뭘까?

생성자는 객체의 필드값을 특정한 값으로 초기화가 필요할때 사용한다.

 

생성자도 다른 메소드와 마찬가지로 오버 로딩이 가능하다. 따라서 다양한 버전의 생성자를 만들어놓을 수 있다.

 

 

class Menu
{
    public Menu()
    {
    	Name="";
        Price=0;
    }
    
    public Menu(string _Name, int _Price)
    {
    	Name=_Name;
        Price=_Price;
    }
    
    
   //필드
    public string Name;
    public int Price;
 
   //메소드
    public void Order()
    {
    	Console.WriteLine("{0}을 주문했습니다.",Name);
    }
}

 

 

생성자를 구현할 때에는 한 가지 주의사항이 있다. 

생성자를 하나라도 직접 구현해놓으면 컴파일러는 매개변수가 없는 기본 생성자를 제공하지 않는다.

 

기본 생성자가 프로그래머의 의도와는 다르게 객체를 초기화 해버리는 일을 방지하기 위해서이다.

 

 

이제 예제 코드를 살펴보자.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Csharp_Project
{
    class Menu
    {
        public Menu()
        {
            Name = "";
            Price = 0;
        }

        public Menu(string _Name, int _Price)
        {
            Name = _Name;
            Price = _Price;
        }

        //필드
        public string Name;
        public int Price;

        //메소드
        public void Order()
        {
            Console.WriteLine("{0}를 주문했습니다.", Name);
        }
    }
    class Class_EX_2_
    {
        static void Main(string[] args)
        {
            Menu pizza = new Menu();
            pizza.Name = "피자";
            pizza.Price = 15000;
            pizza.Order();
            Console.WriteLine("{0}의 가격은 {1}원 입니다.", pizza.Name, pizza.Price);

            Menu coke = new Menu("콜라", 2000);
            coke.Order();
            Console.WriteLine("{0}의 가격은 {1}원 입니다.", coke.Name, coke.Price);
        }
    }
}

 

 

<실행 결과>

 

 

 

 


 

종료자는 클래스의 이름 앞에 ~를 붙여 선언한다.

종료자는 매개변수도 없고 한정자도 사용하지 않으며 오버로딩도 불가능하다. 직접 호출도 할 수 없다.

종료자는 CLR의 가비지 컬렉터가 객체가 소멸되는 시점을 판단해 종료자를 자동으로 호출해준다.

 

하지만 C#에서는 가급적 종료자를 사용하지 않는 것이 좋다. 이유를 몇 가지 추려보면 다음과 가탇.

 

1. 가비지 컬렉터가 언제 동작할지 예측할 수 없다. 따라서 프로그래머가 원하는 타이밍에 종료자를 호출하는 것이 어렵다.

 

2. 종료자를 명시적으로 구현하면 가비지 컬렉터는 클래스의 족보를 타고 올라가 객첼부터 상속받은 Finalize() 메소드를 호출한다. 하지만 이렇게 하면 프로그램의 성능이 저하될 수 있다.

 

3. 종료자를 사용하지 않아도 가비지 컬렉터를 통해 객체의 소멸을 처리할 수 있다.

 

 

가비지 컬렉터는 C#의 장점중 하나이다. 때문에 C#에서는 굳이 소멸자를 이용할 필요가 없다.