클래스, 생성자, 깊은복사/얕은복사 ,상속

doyeon kim·2025년 1월 20일

C#

목록 보기
3/13

클래스

실세계에 존재하는 사물, 혹은 개념을 모델링한 것
-> 건축물의 설계도, 붕어빵 틀

클래스 선언

접근제한자 class 클래스명

{
속성 정의; // 프로퍼티
행위 정의; // 함수

}

class AA
    {
        public int a;
        public int b;
        private int c;

        public void setC()
        {
            c = 10;
            a = 20;
        }

        public int getC()
        {
            return c;
        }

    }
    
class Program
    {

        static void Main(string[] args)
        {

            AA AClass = new AA();

            AClass.setC();
            Console.WriteLine($"c 값은 {AClass.getC()}");

            Console.ReadKey();
        }
    }

AA : 클래스(붕어빵틀), AClass : 인스턴스(붕어빵)

접근제한자 private 나 기본 설정 시 외부에서 접근 불가(c 는 바로 접근X => setC()로 접근 )

클래스 참조

 static void Main(string[] args)
        {

            AA AClass = new AA();

            AClass.a = 10;
            AA AClass2 = AClass;
            AClass2.a = 20;

            Console.WriteLine("a 값은 {0}", AClass.a);
           
            Console.ReadKey();
        }
        => 출력 시 10 아니라 20이 나옴

ref 함수와 마찬가지로 클래스 참조 시 해당 값이 아닌 주소를 참조
=> AClass.a 와 AClass2.a 가 같은 주소 가리킴

클래스 형변환

 public class Parent
    {
        public int m_A = 1;
    }

    public class Child : Parent
    {
        public int m_A = 10;
        public int m_B = 20;
    }

class Program
    {

        static void Main(string[] args)
        {
            Parent pp = new Parent();
            Child cc = new Child();

            Parent pcc = (Parent)cc;

            Console.WriteLine(cc.m_A);
            Console.WriteLine(pcc.m_A);



            Console.ReadKey();
        }
    }
    
    출력
    10
    1

Child 를 생성하면 그림과 같이 메모리 할당
=> cc.m_A 는 자식 클래스의 변수가 출력

자식 클래스를 부모 클래스로 변환 시 (Parent pcc = (Parent)cc;) 그림과 같이 인식함
(메모리상에는 m_B 가 할당되어 있지만 인식하지 못하고 없는 변수로 나옴)
=> pcc.m_A 출력 시 부모 클래스의 변수가 출력


인스턴스

설계도면(클래스) 에 의해 생성된 객체
-> 메모리 영역에 클래스의 형태로 할당된다.


생성자

public AA(int p_a, int p_b, int p_c)  // => 생성자
        {
            a = p_a;
            b = p_b;
            c = p_c;
        }

        public AA()   // => 기본 생성자
        {
            a = 10;
            b = 20;
            c = 30;
        }
        
class Program
    {

        static void Main(string[] args)
        {

            AA AClass = new AA(1,2,3);
            AA AClass2 = new AA();

            Console.WriteLine("AClass의 a 값은 {0}", AClass.a);
            Console.WriteLine($"AClass2의 a 값은 {AClass2.a}");

            Console.ReadKey();
        }
    }
    
    => AClass의 a 값은 1
    => AClass2의 a 값은 10
    

static

class AA
    {

        public int a;
        public int b;
        private int c;
        public static int d;  
       
        public static int Add(int p_a)
        {
            return p_a + AA.d;
        }
     }  
     
          
static void Main(string[] args)
        {

            AA AClass = new AA(1,2,3);
            AA AClass2 = new AA();
            int result = AA.Add(AClass.a);


            Console.WriteLine("a 값은 {0}", AClass.a);
            Console.WriteLine($"AClass2의 a 값은 {AClass2.a}");
            Console.WriteLine($"AClass의 d 값은 {AA.d}");
            Console.WriteLine("계산결과 : {0}", result);

static 의 경우 독립된 저장 공간을 가지고 호출 시 클래스명.변수(메서드)명 식으로 호출


얕은 복사 & 깊은 복사

얕은 복사

using System;

class ShallowCopyExample
{
    public class Person
    {
        public string Name; // 참조 타입
        public int Age;     // 값 타입
    }

    static void Main(string[] args)
    {
        // 원본 객체 생성
        Person person1 = new Person { Name = "Alice", Age = 30 };

        // 얕은 복사 수행 => person1 의 주소값이 person2에 전달됨
        Person person2 = person1;

        // person2의 값을 변경
        person2.Name = "Bob"; // 참조 타입 변경
        person2.Age = 25;     // 값 타입 변경

        // 결과 출력
        Console.WriteLine($"Person1: Name = {person1.Name}, Age = {person1.Age}");
        Console.WriteLine($"Person2: Name = {person2.Name}, Age = {person2.Age}");
    }
}

결과
Person1: Name = Bob, Age = 25
Person2: Name = Bob, Age = 25

얕은 복사 시 person1과 person2 가 같은 객체를 참조하게 됨
=> 둘 중 하나만 변경해도 나머지 같이 변경

깊은 복사

using System;

class DeepCopyExample
{
    public class Person
    {
        public string Name;  // 참조 타입
        public int Age;      // 값 타입

        // 깊은 복사를 수행하는 메서드
        public Person DeepCopy()
        {
            return new Person
            {
                Name = string.Copy(Name), // 새로운 문자열 복사
                Age = Age                // 값 타입 그대로 복사
            };
        }
    }

    static void Main(string[] args)
    {
        // 원본 객체 생성
        Person person1 = new Person { Name = "Alice", Age = 30 };

        // 깊은 복사 수행 => person1 의 실제 값이 person2에 전달
        Person person2 = person1.DeepCopy();

        // person2의 값을 변경
        person2.Name = "Bob"; // 참조 타입 변경
        person2.Age = 25;     // 값 타입 변경

        // 결과 출력
        Console.WriteLine($"Person1: Name = {person1.Name}, Age = {person1.Age}");
        Console.WriteLine($"Person2: Name = {person2.Name}, Age = {person2.Age}");
    }
}

결과
Person1: Name = Alice, Age = 30
Person2: Name = Bob, Age = 25

깊은 복사 시 새 객체를 생성해서 원본 객체의 주소가 아닌 값을 할당하는 방식
=> 원본 객체 값을 변경해도 person2 값은 변하지 않음


클래스 상속

부모 클래스가 가지고 있는 기능을 상속받은 자식 클래스에서 사용

public class AA
    {
        protected int a;
        public int b;

    }

public class AA1 : AA
    {
       public int c;
    }

public class AA2 : AA1
    {
        protected void FN()
        {
            a = 10;
        }
        public int d; 
    }
    
    
class Program
    {

        static void Main(string[] args)
        {

            AA2 AClass = new AA2();
            AClass.b = 20;
            AClass.a = 1;  => 접근제한 때문에 에러

            Console.ReadKey();
        }
    }

클래스명 : 상속받는 클래스명 형태로 선언

protected

자신과 상속받는 클래스 내부에서 사용할 수 있는 접근제한자
=> 변수 a의 경우 AA,AA1,AA2 에서 호출 가능 but main 함수에선 불가능

클래스 상속 예시

 public class Animal
    {
        public string Name;
        public void Nurse()
        {
            Console.WriteLine($"{Name} 을 치료");
        }
    }

    public class Dog : Animal
    {
        public Dog(string p_name)
        {
            Name = p_name;
        }

       public void Bark()
       {
            Console.WriteLine($"{Name} 짖다");
       }

    }

    public class Cat : Animal
    {
        public Cat(string p_name)
        {
            Name = p_name;
        }

        public void meow()
        {
            Console.WriteLine($"{Name} 울다");
        }
    }
    
    class Program
    {

        static void Main(string[] args)
        {
            Dog dog = new Dog("누렁이");
            dog.Nurse();
            dog.Bark();


            Cat cat = new Cat("나비");
            cat.Nurse();
            cat.meow();

            Animal animal = dog;    
            // => 자식 타입을 부모 타입으로 형변환 가능, 단 bark 메서드는 사용 불가
            Animal animal1 = new Animal();  
            Dog dog2 = (Dog)animal1; => 부모 타입을 자식 타입으로 변환은 불가능
			
            // 이런식으로 형변환은 가능
           animal = dog; 
           //animal 은 자식인 dog 를 가리키고 있지만 타입이 Animal이라 bark 불가
           
           Dog dog2 = animal as Dog
				
            Console.ReadKey();
        }
    }

자식 타입에 해당하는 메서드의 주소가 부모 타입에서는 할당되어있지 않아서 문법상 에러는 없지만 실행 시 오류 발생


virtual

자식 -> 부모 클래스 형변환 시 Call 함수 호출하면 부모의 메서드가 사용된다. 이 때 자식 클래스의 메서드를 사용하고 싶으면

이런식으로 부모 클래스에 virtual, 자식 클래스에 override 추가
(부모 클래스에서 선언, 자식 클래스에서 재정의)
※ 단 둘의 매개변수와 접근제한자가 같아야 됨

※ 부모 클래스의 call 을 사용하고 싶으면 상속받는 클래스에서 base.call() 로 호출


확장 메서드

public class MyClassType
    {
        public int m_A = 0;
        public void Add(int p_a)
        {
            m_A += p_a;
            Console.WriteLine($"덧셈결과 : {m_A}");
        }
    }
    

여기서 뺄셈 기능을 추가하고 싶은데 해당 클래스를 건드릴 수 없을 때

static 클래스와 메서드를 추가한 뒤 매개변수에 this 클래스명 변수명 작성

결과

 class Program
    {

        static void Main(string[] args)
        {
            MyClassType tcls = new MyClassType();
       
            tcls.Add(10);
            tcls.Minus(5);
			//MyClassType 에는 Add 밖에 없지만 Minus 메서드 사용 가능

            Console.ReadKey();
        }
    }

0개의 댓글