일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 구조체
- Data Structure
- C언어 포인터
- docker
- 도커
- dart 언어
- c#
- github
- c# winform
- c언어
- 깃
- git
- c# 추상 클래스
- Python
- vim
- 포인터
- Houdini
- Algorithm
- jupyter lab
- jupyter
- HTML
- C# delegate
- 유니티
- gitlab
- C++
- Flutter
- c# 윈폼
- 플러터
- 다트 언어
- Unity
- Today
- Total
목록분류 전체보기 (507)
nomad-programmer
추상 클래스는 인터페이스와 달리 "구현"을 가질 수 있다. 하지만 클래스와는 달리 인스턴스를 가질 수 없다. 한마디로 추상 클래스는 구현을 갖되 인스턴스는 만들지 못한다. abstract class 클래스이름 { // 클래스와 동일하게 구현 } 추상 클래스가 인터페이스와 클래스의 중간에 있다고 했지만 추상 클래스는 클래스에 더 가깝다. 추상 클래스의 접근성이 그 예이다. 클래스와 똑같다. 인터페이스에서는 모든 메소드가 public으로 선언되는 반면, 클래스는 한정자를 명시하지 않으면 모든 메소드가 private로 선언된다. 한편, 추상 클래스에는 인스턴스를 만들 수 없는 점 외에도 클래스와 다른 점이 또 하나 있다. 그것은 바로 "추상 메소드 (Abstract Method)" 를 가질 수 있다는 사실이다..
![](http://i1.daumcdn.net/thumb/C150x150/?fname=https://blog.kakaocdn.net/dn/IF6Io/btqIwDnxX0j/uCSQWf5TIbE6CR0CwXTQh0/img.png)
C#에서 클래스는 여러 클래스를 다중 상속할 수 없다. 이른바 "죽음의 다이아몬드" 라는 문제 때문이다. 죽음의 다이아몬드란, 조부모 클래스를 두 개의 파생 클래스가 상속하고, 이 두 개의 파생 클래스를 다시 하나의 자식 클래스가 상속하는 것을 말한다. 죽음의 다이아몬드 문제의 핵심은 "모호성" 이다. 컴파일은 MyVehicle이 어느 Ride() 메소드를 물려받도록 할까? Car의 Ride()? 아니면 Plane의 Ride()? 죽음의 다이아몬드 문제의 핵심은 모호성이다. 컴파일러 상태에 따라 Car의 Ride()가 물려받을 수도 혹은 Plane의 Ride()가 물려받을 수 있다. 클래스를 다중 상속하면 문제가 하나 더 발생한다. 바로 "업-캐스팅 (Up-Casting)" 문제이다. 다중 상속이 허용된..
인터페이스를 상속할 수 있는 것은 클래스뿐이 아니다. 구조체는 물론이고, 인터페이스도 인터페이스를 상속할 수 있다. 기존의 인터페이스에 새로운 기능을 추가한 인터페이스를 만들고 싶을 때 인터페이스를 상속하는 인터페이스를 만들면 된다. 기존에 존재하는 인터페이스에 새로운 기능을 추가해도 되지만 다음의 경우처럼 인터페이스를 수정할 수 없을 때에는 인터페이스를 상속하는 인터페이스를 이용해야 한다. 상속하려는 인터페이스가 소스 코드가 아닌 어셈블리로만 제공되는 경우 : .NET Framework SDK에서 제공하는 인터페이스들이 그 예이다. 어셈블리 안에 있기 때문에 인터페이스를 수정할 수 없다. 이인터페이스에 새로운 기능을 추가한 인터페이스를 만들고 싶으면 상속하는 수밖에 없다. 상속하려는 인터페이스의 소스 ..
인터페이스는 객체 지향 프로그래밍을 한층 더 강력하게 만들어주는 요소이다. 객체 지향 프로그래밍의 꽃이라고도 불리며 객체 지향 프로그래밍의 고수는 인터페이스를 잘 활용할 수 있어야 한다고 말하기도 한다. 인터페이스의 선언 인터페이스(Interface)는 다음과 같이 interface 키워드를 이용하여 선언한다. interface 인터페이스이름 { 반환형식 메소드이름1(매개 변수 목록); 반환형식 메소드이름2(매개 변수 목록); . . . } // 인터페이스 실제 정의 예 interface ILogger { void WriteLog( string log ); } 클래스를 선언하는 것과 비슷하다. 허나 메소드, 이벤트, 인덱서, 프로퍼티만을 가질 수 있다. 그런데 그나마도 구현부가 없다. 그리고 클래스는 접..
* Python의 튜플(Tuple)과 똑같다고 생각하면 된다. 튜플도 여러 필드를 담을 수 있는 구조체이다. 하지만 구조체와는 달리 튜플은 형식의 이름을 가지지 않는다. 그래서 튜플은 응용 프로그램 전체에서 사용할 형식을 선언할 때가 아닌, 임시적으로 사용할 복합 데이터 형식을 선언할 때 적합하다. 튜플은 구조체이므로 값 형식이다. 값 형식은 생성된 지역을 벗어나면 스택에서 소멸되기 대문에 프로그램에 장기적인 부담을 주지 않는다는 장점이 있다. // 컴파일러가 튜플의 모양을 보고 직접 형식을 결정하도록 var를 이용해 선언. // 튜플은 괄호 사이에 두 개 이상의 필드를 지정함으로써 만들어진다. var tuple = (123, 789): 위와 같이 이름을 지정하지 않은 튜플을 일컬어 "명명되지 않은 튜플..
C#의 복합 데이터 형식에는 클래스 말고도 구조체(Structure)라는 것이 있다. 구조체는 클래스하고 사촌지간쯤 된다. 필드와 메소드를 가질 수 있는 등 상당 부분 비슷하다. struct 구조체이름 { // 필드, ... // 메소드, ... } struct MyStruct { public int field1 public int field2 public void MyMethod() { } } 구조체에서 public을 많이 사용하는 이유? 문법적으로 클래스와 유사하기는 해도, 각자의 존재의 이유는 조금 다르다. 클래스는 실세계의 객체를 추상화하려는데 그 존재의 이유가 있지만, 구조체는 데이터를 담기 위한 자료 구조로 사용된다. 따라서 굳이 은닉성을 비롯한 객체 지향의 원칙을 구조체에 강하게 적용하지는 ..
확장 메소드(Extension Method)는 기존 클래스의 기능을 확장하는 기법이다. 부모 클래스를 물려받아 파생 클래스를 만든 뒤 여기에 필드나 메소드를 추가하는 상속과는 다르다. 확장 메소드는 이미 존재하는 클래스의 기능을 확장한다. 확장 메소드를 이용하면 string 클래스에 문자열을 뒤집는 기능을 넣을 수도 있고, int 형식에 제곱 연산 기능을 넣을 수도 있다. 확장 메소드를 선언하는 방법 메소드를 선언하되, static 한정자로 수식해야 한다. 그리고 이 메소드의 첫 번째 매개 변수는 반드시 this 키워드와 함께 확장하고자 하는 클래스(형식)의 인스턴스여야 한다. 그 뒤에 따라오는 매개 변수 목록이 실제로 확장 메소드를 호출할 때 입력되는 매개 변수이다. 메소드는 클래스 없이 선언될 수 없..
분할 클래스(Partial Class)란, 여러 번에 나눠서 구현하는 클래스를 말한다. 분할 크래스는 그 자체로 특별한 기능을 하는 것은 아니고, 클래스의 구현이 길어질 경우 여러 파일에 나눠서 구현할 수 있게 함으로써 소스 코드 관리의 편의를 제공하는 데 그 의미가 있다. using System; namespace CSharpExample { partial class MyClass { public void Method1() { Console.WriteLine("method1"); } public void Method2() { Console.WriteLine("method2"); } } // 클래스 이름이 등일해야 한다. partial class MyClass { public void Method3() ..
중첩 클래스(Nested Class)는 클래스 안에 선언되어 있는 클래스를 말한다. class OuterClass { class NestedClass { } } 중첩 클래스를 선언하는 문법은 매우 간단하다. 클래스 안에 클래스를 선언하는 것이 전부다. 객체를 생성하거나 객체의 메소드를 호출하는 방법도 보통의 클래스와 다르지 않다. 한 가지 중첩 클래스가 다른 클래스와 다른 점이 있다면, 자신이 소속되어 있는 클래스의 멤버에 자유롭게 접근할 수 있다는 사실이다. private 멤버에도 접근할 수 있다. 중첩 클래스를 사용하는 이유 클래스 외부에 공개하고 싶지 않은 형식을 만들고자 할 때 현재 클래스의 일부분처럼 표현할 수 있는 클래스를 만들고자 할 때 다른 클래스의 private 멤버에도 접근할 수 있는 ..
클래스를 상속이 안 되도록 봉인하는 것처럼 메소드도 오버라이딩되지 않도록 봉인할 수 있다. 그렇다고 모든 메소드를 봉인할 수 있는 것은 아니다. virtual로 선언된 가상 메소드를 오버라딩한 버전의 메소드만이 가능하다. using System; namespace CSharpExample { class Base { public virtual void SealMe() { } } class Derived : Base { public sealed override void SealMe() { base.SealMe(); } } class WantToOverride : Derived { public override void SealMe() { } } class MainApp { static int Main(stri..
메소드 숨기기란, CLR에게 부모 클래스에서 구현된 버전의 메소드를 감추고 파생 클래스에서 구현된 버전만을 보여주는 것을 말한다. 메소드 숨기기는 파생 클래스 버전의 메소드를 new 한정자로 수식함으로써 할 수 있다. (생성자를 호출할 때 사용하는 new 연산자와는 다르다) 메소드 숨기기는 오버라이딩과 다르다. 이름 그대로 메소드를 숨기고 있을 분이다. 메소드 숨기기는 완전한 다형성을 표현하지 못하는 한계를 갖고 있다. 따라서 부모 클래스를 설계할 때는 파생 클래스의 모습까지 고려해야 한다. using System; namespace CSharpExample { class Base { public void MyMethod() { Console.WriteLine("Base.MyMethod()"); } } ..
객체 지향 프로그래밍에서 다형성(Polymorphism)은 객체가 여러 형태를 가질 수 있음을 의미한다. 다형성은 원래 하위 형식 다형성(Subtype Polymorphism)의 준말이다. 다시 말해, 자신으로부터 상속받아 만들어진 파생 클래스를 통해 다형성을 실현하는 것이다. 메소드를 오버라이딩하기 위해서는 한 가지 조건이 필요하다. 그 조건은 바로 오버라이딩을 할 메소드가 virtual 키워드로 한정되어 있어야 한다는 것이다. private로 선언한 메소드는 오버라이딩할 수 없다. private로 선언된 멤버는 어차피 파생 클래스에서 보이지도 않는다. 같은 이름, 같은 형식, 같은 매개 변수를 이용하여 선언했다 하더라도 컴파일러는 메소드를 재정의한다고 생각하지 않고 전혀 없었던 메소드를 선언했다고 간..
C#은 C++의 순수 가상 함수같은 말이 없다. 허나 순수 가상 함수와 같은 기능을 하는 것을 만들 수 있다. 그것은 추상 클래스를 정의하고 abstract 키워드를 사용하는 것이다. 추상 클래스와 가상 함수의 차이점 가상 함수는 재정의를 하지 않아도 컴파일 에러가 발생하지 않는다. 또한 부모 클래스에서 가상 함수는 {} 괄호가 있어야 한다. 즉, 함수 같은 구조여야 한다. public virtual string GetData() { return ""; } 추상 클래스의 추상화 함수는 재정의를 하지 않으면 컴파일 에러가 발생한다. 그리고 부모 클래스에서 {} 괄호는 필요치 않다. 선언만 존재하면 된다. public abstract string GetData(); 추상 클래스는 독자적으로 인스턴스를 생성할..
개와 고양이는 종은 다르지만 젖을 먹인다는 공통점이 있다. 그러기에 포유류로 분류된다. 이를 플래스로 다음과 같이 표현할 수 있다. class Mammal { public void Nurse() {} } class Dog : Mammal { public void Bark() {} } class Cat : Mammal { public void Meow() {} } // ///////////////////////// Mammal mammal = new Dog(); mammal.Nurse(); Dog dog = (Dog)mammal; mammal = new Cat(); mammal.Nurse(); 위의 예처럼 부모 클래스와 파생 클래스 사이를 왔다 갔다 형식 변환이 가능하다. 파생 클래스의 인스턴스는 부모 클..
class 부모 클래스 { // 멤버 선언 } class 파생 클래스 : 부모 클래스 { // 아무 멤버를 선언하지 않아도 부모 클래스의 모든 것을 물려받아 갖게 된다. // 단, private로 선언된 멤버는 예외이다. } 파생 클래스의 이름 뒤에 콜론(:)을 붙여주고 그 뒤에 상속받을 부모 클래스의 이름을 붙여주면 된다. 만약 부모 클래스의 생성자가 매개 변수를 입력받도록 선언되어 있다면 파생 클래스의 인스턴스를 생성할 때 호출되는 부모 클래스의 생성자에게 어떻게 매개 변수를 전달해줄 수 있을까? 이럴 때는 base 키워드를 사용하면 된다. this 키워드가 "자기 자신"을 가리킨다면 base는 "부모 클래스"를 가리킨다. this를 통해 자기 자신의 멤버에 접근할 수 있었던 것처럼, base 키워드..
객체 지향 프로그래밍에서 필요한 최소의 기능만을 노출하고 내부를 감출 것을 요구한다. 이것을 은닉성이라고 한다. 객체 지향 프로그래밍의 3대 특성 객체 지향 프로그래밍에는 여러 가지 특성이 있다. 그 중에서도 3대 특성으로 꼽히는 것들이 있다. 이 셋중의 하나가 바로 은닉성이며 나머지 2개는 상속성(Inheritance)과 다형성(Polymorphism)이다. 클래스에 선언되어 있는 필드와 메소드 중에 어떤 것들은 사용자에게 노출할 것이 있는가 하면, 절대로 노출시키지 말아야 하는 것들도 있다. 특히 필드는 상수를 제외하곤 "무조건" 감추는 것이 좋다. 접근 한정자 (Access Modifier)는 감추고 싶은 것은 감추고, 보여주고 싶은 것은 보여줄 수 있도록 코드를 수식하며, 필드, 메소드를 비롯해 ..
class MyClass { int a, b, c; public MyClass() { this.a = 555; } public MyClass(int b) { this.a = 555; this.b = b; } public MyClass(int b, int c) { this.b = b; this.c = c; } } 위의 코드는 문제가 있는 코드인가? 그렇지 않다. 문법적으로도 문제가 없고 동작도 잘 될 것이다. 다만 MyClass() 생성자 안에 똑같은 코드가 중복되어 들어가 있다. 비 생성적이다. 이런 고민을 해결해주는 것이 this() 이다. this가 객체 자신을 지칭하는 키워드인 것처럼, this() 는 자기 자신의 생성자를 가리킨다. this() 는 생성자에서만 사용될 수 있다. 그것도 생성자의 코..
데이터 형식에서 참조 형식과 값 형식이 무엇인지 정확히 알아야 한다. 참조 형식은 힙 영역에 객체를 할당하고 스택에 있는 참조가 힙 영역에 할당된 메모리를 가리킨다. 클래스는 태생이 참조 형식이다. 그래서 객체를 복사할 때 주의해야 한다. 무작정 대입 연산으로 복사되었다고 생각하면 안된다. 왜냐면 참조 형식이기 때문에 변수는 힙 영역에 할당된 객체의 주소 값만을 지니고 있다. 이것을 다른 변수에 대입하면 해당 주소만 넘겨주는 꼴이된다. 그럼 그 다른 변수도 힙 영역의 주소를 가리키게되므로 서로 공유된 메모리를 갖게된다. 이렇게 객체를 복사할 때 참조만 살짝 복사하는 것을 "얕은 복사 (shallow copy)"라고 한다. 원본으로부터 별도의 힙 공간에 객체를 복사하는 것은 "깊은 복사 (deep copy..
객체는 생성자에 의해 만들어지고 소멸자에 의해 사라진다. 생성자 생성자는 클래스와 이름이 같고, 반환 형식이 없다. 생성자의 임무는 단 한 가지, 해당 클래스의 객체를 생성하는 것뿐이다. 그러기에 반환할 것이 없다. 클래스를 선언할 때 명시적으로 생성자를 구현하지 않아도 컴파일러에서 생성자를 만들어준다 (이런 생성자를 기본 생성자라 한다). 객체를 다루다 보면 객체를 생성하는 시점에 객체의 필드를 원하는 값으로 초기화하고 싶을 때가 이런 작업을 할 수 있는 최적의 장소가 바로 객체를 생성하기 위해 호출하는 메소드인 생성자이다. 생성자도 오버로딩이 가능하다 소멸자 종료자의 이름은 클래스 이름 앞에 ~를 붙인다. 그리고 소멸자는 생성자와는 달리 매개 변수도 없고, 한정자도 사용하지 않는다. 또한 여러 버전의..
객체를 생성할 때 다음과 같이 코드를 작성한다. Cat kitty = new Cat(); 위 문장의 가장 끝에 있는 Cat()는 생성자(Constructor) 라고 하는 특별한 메소드이다. 생성자는 클래스의 이름과 동일한 이름을 가지며, 객체를 생성하는 역할을 한다. Cat() 생성자 앞에 있는 new 키워드는 생성자를 호출해서 객체를 생성하는 데 사용하는 연산자이다. 말하자면 new 연산자와 생성자는 바늘과 실 같은 존재라고 할 수 있다. heap 메모리 공간에 객체를 생성한다. 그러니 당연하게도 할당시키는 연산자 new를 써야 한다. C++ 언어에서 new, C언어에서 malloc과 같다고 생각하면 된다. Cat 클래스를 비롯한 모든 클래스는 복합 데이터 형식이다. 그리고 복합 데이터 형식은 참조 형..