타입 정의는 변수 선언을 단순화하지만, 타입의 내부 구조를 외부로부터 숨기지는 않는다. 즉, 같은 타입의 변수를 선언할 수 있는 어떤 서브루틴이라도 해당 타입의 내부 구성 요소에 직접 접근할 수 있다. 캡슐화의 목적은 타입의 내부 구조에 대한 직접적인 접근을 방지하는 것이다.
private 선언을 사용하여 타입의 내부 구조를 패키지 외부로부터 숨길 수 있다.
private와 protected로 보호 가능하다. protected는 상속 데이터들을 위해 존재한다.
상속은 객체 지향 프로그래밍에서 정보를 한 객체에서 다른 객체로 자동 전달하는 메커니즘을 제공한다. 이는 데이터의 "범위(scope)"를 제공하는 방식으로, 언어의 문법적 범위와 유사한 기능을 한다.
객체 지향 언어에서는 데이터 상속이 파생 클래스를 통해 명시적으로 구현된다. 이는 정적 범위와 유사하게, 이름이 중첩된 절차를 통해 암시적으로 알려진다.
위의 경우 complex 클래스는 rational의 멤버로 자신의 멤버 함수를 정의하고, 기능을 확장한다.
rational 클래스에 정의된 public 또는 protected 멤버는 complex 클래스에서 사용 가능하다. 예를 들어, error 함수는 complex 클래스에 상속되므로, complex 객체에서 error 함수를 호출할 수 있다. 하지만 rational의 error 함수가 호출될 때, 호출된 문맥이 rational인지 complex인지를 알아야 하는 경우 문제가 발생할 수 있다.
기본 클래스(rational)에서 name 함수를 가상 함수로 정의하여 파생 클래스(complex)에서 재정의할 수 있다. 이렇게 하면 error 함수가 호출될 때, 현재 객체의 name 함수가 호출되어, 객체가 rational인지 complex인지를 정확히 반영할 수 있다.
가상 함수는 각 객체에 런타임에 대한 기술자를 포함하게 하여, 실행 시에 어떤 함수를 호출할지 결정한다.
rational::error 함수에서 객체의 각 부분(num과 den)의 값을 출력하고자 할 때, 추가적인 가상 함수를 사용할 수 있다. this 포인터는 현재 객체를 가리키므로, *this를 사용하여 현재 객체의 counter 객체에 접근할 수 있다.
C++에서 정적 상속은 때때로 한계가 있을 수 있으며, 이를 해결하기 위해 친구 클래스를 사용할 수도 있다. 예를 들어, complex 클래스 내의 realpt가 비공개(private) 데이터일 경우, thing 클래스가 A.realpt에 접근하기 위해 complex 클래스 내에 friend class thing을 선언할 수 있다. 이러한 방식은 thing 클래스가 complex 클래스의 숨겨진 부분에 접근할 수 있게 하지만, C++의 정적 상속을 우회하는 것으로 간주된다.
특정 특징을 여러 클래스에 동시에 추가하고자 할 때 혼합 상속을 사용할 수 있다. C++에는 없는 개념이다.
같은 이름을 가진 두 연산이 다른 매개변수(기초)를 가질 때 오버로딩이라고 한다.
C++에서, 같은 이름의 함수가 매개변수가 다를 경우, 이는 오버로딩으로 간주된다. 예를 들어, 매개변수가 기본값을 가진 함수와 다른 함수가 있을 경우, 호출하는 맥락에 따라 올바른 함수가 선택된다.
하나의 연산이나 서브루틴이 다양한 타입의 변수에 대해 다른 행동을 보일 수 있게 하는 프로그래밍 언어의 특징이다.
다형성은 여러 형태가 있다.
예시로 reverse 함수는 다양한 타입의 리스트에 적용되며, 입력 타입에 따라 반환 타입이 다르다. identity는 어떤 입력에도 적용되고, 입력 타입을 그대로 반환한다.
하지만 ML에서 fun add(x, y) = x + y; 함수는 다형성이 아닌 것으로 간주된다. 이는 ML이 강력한 타입 시스템을 가지고 있기 때문에, x와 y의 타입이 명확하지 않으면 오류가 발생할 수 있기 때문이다.
ML은 강타입 언어이지만, 타입을 명시적으로 선언하지 않아도 타입 추론을 통해 타입을 결정 가능하다. add 함수의 예에서, 모든 정의는 타입 추론을 통해 서로 동등하게 처리될 수 있다. 하지만, 타입이 명시되지 않은 add 함수는 모호할 수 있으므로, ML에서는 타입을 명확하게 하기 위해 타입 추론을 사용한다.
int와 real 사이의 자동 타입 변환을 허용하지 않기에, fun add(x, y) = real(x) + y;처럼 명확히 타입을 지정해야 한다.
매개변수화된 객체를 통해 진정한 다형성을 생성할 수 있다. 예를 들어 스택 객체는 stack(int, 10) 또는 stack(float, 20)처럼 다양한 타입을 수용 가능하다.
정적 타입 언어(예: ML, C++)에서는 컴파일러가 함수의 매개변수 타입을 기호 테이블에 저장하여 쉽게 다형성을 구현할 수 있다.
동적 타입 언어에서는 매개변수를 전달할 때 직접적인 서술자(直接描述子)나 박스형 서술자(boxed, 装箱 descriptor)를 사용할 수 있다.
박스형 서술자는 매개변수 필드에 타입 서술자를 포함하며, 나머지 부분은 실제 객체의 주소를 담고 있다.
예를 들어 매개변수 서술자 필드가 5바이트인 경우, 다양한 타입의 데이터(정수, 문자, 불리언, 복잡한 레코드)를 다형성 함수에 전달할 수 있다. 각 타입에 따라 첫 번째 바이트는 타입을 나타내고, 나머지 바이트는 데이터나 객체의 주소를 저장한다.