'분류 전체보기'에 해당되는 글 116건

  1. 2018.04.01 [Lua] 클래스
  2. 2018.03.28 [Lua] 메타테이블과 메타메서드
  3. 2018.03.28 [Lua] 반복자
  4. 2018.03.28 [Lua] 함수의 내부
  5. 2018.03.27 [Lua] 함수
  6. 2018.03.27 [Lua] 문장
  7. 2018.03.27 [Lua] 표현식
  8. 2018.03.27 [Lua] 타입과 값
  9. 2018.03.27 [Lua] 기본 문법
  10. 2017.12.25 [C++] 템플릿(Template)(2)

[Lua] 클래스

Lua Script 2018. 4. 1. 15:13


Account = { balance = 0 }

function Account.withdraw(value)
    Account.balance = Account.balance - value
end

Account.withdraw(100.0)

위와 같이 테이블에 값을 저장하는 변수와 함수를 정의해서 객체처럼 정의할 수 있다. 

그런데 withdraw 함수를 보면 "Account." 으로 인해 Account 직접 명시해서 접근하고 있다. 객체지향적 관점(특히 상속)으로 보면 이것은 매우 안좋다. 
다른 언어에서 사용하듯이 lua도 "self" 식별자를 이용해서 자신을 가리키는 메커니즘을 제공한다. 

function Account.withdraw(value)
    self.balance = self.balance - value
end

그렇다면 위 정의가 instance를 생성하려면 어떻게 해야 하는가?
다른 언어에서도 구조가 비슷하듯이 Lua에서 클래스와 인스턴스, 상속등의 구조를 만드려면 metatable을 이용해야 한다. 

앞에 Chapter에서 말했듯이, table은 table마다 metatable을 다르게 가져갈 수 있다. 하지만 객체 지향 구조를 만들기 위해서는 instance가 되는 table들이 같은 Class table을 metatable로 가지도록 만들어야 한다. 

Account = { balance = 0 }

function Account:withdraw(value)
  self.balance = self.balance - value
end

function Account:new()
  local instance = { balance = 0 }

  setmetatable(instance, self)
  self.__index = self

  return instance
end

accountA = Account:new()
accountB = Account:new()

accountA:withdraw(30)
accountB:withdraw(20)

print(accountA.balance)
print(accountB.balance)

accountA:withdraw(20)

print(accountA.balance)


위 코드에서 accountA, accountB는 클래스 Account의 instance이고, metatable로 클래스 Account를 가지고 있다. 

accountA:withdraw(30)를 호출하면 accountA 테이블에는 withdraw 함수가 존재하지 않으니 metatable의 __index를 참조해서 withdraw 함수를 찾아서 호출한다. 

즉 getmetatable(accountA).__index.withdraw(30) 이 실행된다. 



'Lua Script' 카테고리의 다른 글

[Lua] 메타테이블과 메타메서드  (0) 2018.03.28
[Lua] 반복자  (0) 2018.03.28
[Lua] 함수의 내부  (0) 2018.03.28
[Lua] 함수  (0) 2018.03.27
[Lua] 문장  (0) 2018.03.27
Posted by 홍성곤
,

Introduction 
: 루아의 모든 테이블과 유저데이터는 각각 메타테이블이 따로있고, 테이블과 유저데이터를 제외한 다른 타입은 하나의 메타테이블을 공유하는 형태다.

새로운 테이블을 생성하면 항상 메타테이블이 없이 생성된다. 

t = {}
print(getmetatable(t)) --> nil 

t1 = {} 
setmetatable(t, t1) 
print(getmetatable(t) == t1) --> true

루아 코드로는 테이블의 메타테이블만 설정할 수 있다. 다른 타입의 메타 테이블을 조작하려면 직접 C 코드를 이용해야 한다. 

그리고 테이블은 자신의 메타테이블이 될 수도 있는데, 이때는 자신의 행위를 기술하는 데 사용된다. 


1. 산술 메타메서드 
: 우선 Set을 구현해보자. 

Set = {} 

function Set.new (l)
    local set = {}
    for _, v in ipairs(l) do set[v] = true end
       
    return set
end

function Set.union (a, b) 
    local res = Set.new{}
    for k in pairs(a) do res[k] = true end
    for k in pairs(b) do res[k] = true end
    
    return res
end

function Set.intersection (a, b) 
    local res = Set.new{}
    for k in pairs(a) do
        res[k] = b[k]
    end

    return res
end

이제 두 집합의 합집합을 구할 때 덧셈 연산자 "+"를 사용하려 한다. 이를 위해 Set을 표현하는 모든 테이블이 하나의 메타테이블을 공유하도록 할 것이다. 이 메타테이블에서 덧셈 연산자를 어떻게 처리해야 하는지 정의해야 한다. 

local mt = {} -- Set에 대한 메타테이블 

다음 단계로, Set을 생성하는 Set.new 함수를 수정한다. 

function Set.new (l)
    local set = {}
    setmetatable(set, mt)
    for _, v in ipairs(l) do set[v] = true end 
       
    return set
end

마지막으로 메타테이블에 덧셈을 처리하는 방법을 설멸하는 메타메서드인 "__add" 추가한다. 

mt.__add = Set.union

s1 = Set.new{10, 20, 30, 50}
s2 = Set.new{30, 1}
s3 = s1 + s2
Set.print(s3) --> {1, 10, 20, 30, 50}

동일한 방식으로 곱셈 연산자를 이용해서 교집합을 구하도록 할 수 있다. 

mt.__mul = Set.intersection
Set.print((s1 + s2) * s1) --> {10, 20, 30, 50} 

메타테이블에는 산술 연산자에 대응하는 필드명이 있다. __add, __mul 외에 뺄셈을 위한 __sub, 나눗셈을 위한 __div, 부정 연산을 위한 __unm, 나머지 연산을 위한 __mod, 지수 연산을 위한 __pow가 있다. 그리고 붙이기 연산을 위한 __concat 필드도 정의할 수 있다. 


2. 관계 메타메서드 
: 메타테이블을 이용하면 관계 연산자에도 의미를 부여할 수 있다. 
- __eq : = 
- __lt : < (작음)
- __le : <= (작거나 같음) 
위 세개의 관계 연산자는 별도의 메타메서드로 제공되지는 않는다. 대신 루아는 a~=b를 not(a==b)로 표현하듯이 not을 써서 표현할 수 있다. 


3. 라이브러리에 정의된 메타메서드
: print() 함수는 인자를 받으면 tostring을 호출해서 출력한다. 
위 Set에 다음과 같은 함수가 있다고 해보자. 

function Set.tostring (set)
    local whole = {}
    for e in pairs(set) do
        whole[#whole + 1] = e
    end
    
    return "{" .. table.concat(whole, ", ") .. "}"
end

위 함수가 있으면 해당 함수를 tostring에 대한 메타메서드로 지정할 수 있다. 

mt.__tostring = Set.tostring

s1 = Set.new{10, 4, 5}
print(s1) --> { 4, 5, 10 }


'Lua Script' 카테고리의 다른 글

[Lua] 클래스  (0) 2018.04.01
[Lua] 반복자  (0) 2018.03.28
[Lua] 함수의 내부  (0) 2018.03.28
[Lua] 함수  (0) 2018.03.27
[Lua] 문장  (0) 2018.03.27
Posted by 홍성곤
,

[Lua] 반복자

Lua Script 2018. 3. 28. 20:41

반복자
: 호출할 때마다 자료구조의 다음 원소를 반환하는 함수를 반복자라고 표현한다. 
우리는 지금부터 클로저를 사용해서 반복자 하나를 만들어 보겠다. 

function values (t)
    local i = 0
    return function ()
                   i = i + 1
                   return t[i]
                end
end

t = {10, 20, 30}
for element in values(t) do 
    print(element)
end

위 values 함수를 클로저를 생성하는 팩토리 함수이다.



'Lua Script' 카테고리의 다른 글

[Lua] 클래스  (0) 2018.04.01
[Lua] 메타테이블과 메타메서드  (0) 2018.03.28
[Lua] 함수의 내부  (0) 2018.03.28
[Lua] 함수  (0) 2018.03.27
[Lua] 문장  (0) 2018.03.27
Posted by 홍성곤
,

[Lua] 함수의 내부

Lua Script 2018. 3. 28. 20:28

Introduction 
: 함수는 숫자나 문자열처럼 전역이나 지역변수에 또는 테이블에 넣을 수 있고, 함수에 인자로 전달이 가능하며, 다른 함수로부터 반환 받을 수도 있다. 

함수는 이름이 없다. 
??? 무슨말인가 하면, 우리가 알고 있는 함수의 이름은 사실 그 함수 주소값을 담고 있는 변수의 이름이다. 

a = {p = print}
a.p("Hello world") --> Hello World
print = math.sin   -- print는 이제 sin 함수를 참조
a.p(print(1))         --> 0.841470
sin = a.p            -- sin은 이제 print 함수를 참조
sin(10, 20)        --> 10 20

그렇다 사실
function foo (x) return 2*x end 문장은 
foo = function (x) return 2*x end 를 보기좋게 썻던 것 뿐이다. 


1. 클로저 
: 클로저는 익명함수 + 비지역 변수 이다. 
익명함수는 알겠는데 비지역 변수는 무엇? 

function newCounter()
    local i = 0
    
    return function ()  -- 익명 함수 
                    i = i + 1
                    return i
                end
end

c1 = newCounter()
print(c1()) --> 1 
print(c1()) --> 2

위 코드에서 c1이 참조하고 있는 익명함수가 호출되는 시점에는 newCounter() 호출이 끝난 상태이기 때문에 지역변수 i의 생명주기가 끝나 버린다. 그런데 어떻게 c1이 참조하고 있는 익명함수에서 지역변수 i를 사용할 수 있는거지?
그것은 블럭이라는 존재 때문이다. 블럭은 익명함수와 위 코드에서 지역변수 i의 값을 저장해서 가지고 있는 놈이라고 보면 된다. 
그럼 아래를 보자. 

c2 = newCounter()
print(c2()) --> 1
print(c1()) --> 3
print(c2()) --> 2 

c1과 c2는 서로 다른 블럭이기 때문에 위와같이 서로다른 인스턴스처럼 동작한다. 

사실 루아에서 우리가 함수라 부르며 호출하는 것들은 함수가 아니라 클로저 이다. 

클로저는 콜백 함수를 구현할 때도 유용하다. 예를들어, 계산기를 만들때 0~9 까지 유사한 버튼을 만들어야 하는데 이런 버튼을 다음과 같은 함수로 만들어 낼 수 있다. 

function digitButton (digit) 
    reutrn Button { label = tostring(digit),
                             action = function ()
                                               addToDisplay(digit)
                                           end
                            }
end

다음은 테이블에 함수를 저장하는 특별한 문법을 소개하겠다. 일반적인 값을 테이블에 넣을 때 사용하는 문법을 써도 되지만, 다음과 같이 사용해도 된다.

Lib = {} 
function Lib.foo (x, y) return x + y end 
function Lib.goo (x, y) return x - y end 




            

'Lua Script' 카테고리의 다른 글

[Lua] 메타테이블과 메타메서드  (0) 2018.03.28
[Lua] 반복자  (0) 2018.03.28
[Lua] 함수  (0) 2018.03.27
[Lua] 문장  (0) 2018.03.27
[Lua] 표현식  (0) 2018.03.27
Posted by 홍성곤
,

[Lua] 함수

Lua Script 2018. 3. 27. 21:31

Introduction
: 루아에서는 루아로 작성한 함수를 물론, C로 작성한(또는 호스트 애플리케이션에서 사용하는 다른 언어로 작성한) 함수도 쓸 수 있다. 실례로 루아 표준 라이브러리의 모든 함수는 C로 작성한 것이다. 

function 함수이름 (매개변수)
    몸체
end

매개변수의 개수와 호출시 전달되는 인자의 개수가 다르면 남는 인자는 버려지고 인자가 모자란 경우에는 해당 매개변수는 nil이 된다. 

1. 여러 값 반환
: lua의 함수는 여러값을 반환할 수 있다. 

function multiReturn(a) 
    return a, a+1
end

local b, c = multiReturn(1)
print(b c) --> 1 2

함수의 반환값과 반환값을 저장하려고 하는 변수의 갯수가 틀린 경우 동적으로 처리해 주는 부분에 대해서 알아 보겠다. 

function foo0() end
function foo1() return "a" end
function foo2() return "a", "b" end

x, y = foo0()  -- x = nil, y = nil
x, y = foo1()   -- x="a", y = nil
x, y, z = foo2() -- x="a", y="b", z=nil

함수 호출이 목록의 끝에 있지 않으면 하나의 값만 반환한다. 
x, y = foo2(), 20 -- x= "a", y = 20
print(foo2() .. "x") --> ax
t = {foo0(), foo2(), 4} -- t[1] = nil, t[2] = "a", t[3] = 4

테이블 생성자는 함수가 반환하는 값을 개수 조정 없이 모두 받아간다. 
t = { foo0() } -- t = {}
t = { foo1() } -- t = {"a"}
t = { foo2() } -- t = {"a", "b"}

다음과 같은 형태도 가능하다. 
function foo(i)
    if i == 0 then return foo0()
    elseif i == 1 then return foo1()
    elseif i == 2 then return foo2()
end

print(foo(0)) --> nil
print(foo(1)) --> a
print(foo(2)) --> a b
print(foo(3)) --> nil

반환값 중에서 딱 하나를 받으려면 다음처럼 ()로 감싸면 된다. 
print((foo0())) --> nil
print((foo1())) --> a
print((foo2())) --> a

* f(g()) 에서 함수 f의 인자 개수가 정해져 있다면, g가 반환하는 개수가 f에 맞게 맞춰진다. 

table.unpack은 다중 반환을 해주는 특수 함수다. 인자로 받은 배열의 원소를 index 1의 원소부터 모두 다중반환 한다. 
a, b = table.unpack({10, 20, 30}) -- a = 10, b = 20 이고 30은 버려짐

unpack을 이용하면 동적으로 어떤 함수든지 임의의 인자로 호출할 수 있다. 
f(table.unpack(a)) -- f는 함수이고 a는 가변 개수의 인자를 담고 있는 배열이다. 

다음과 같이 길이지정도 가능하다. 
print(table.unpack({ "Sun", "Mon", "Tue", "Wed" })) -> Mon Tue 


2. 가변 인자 함수 
: 루아 함수는 가변 인자를 받을 수 있다. 

function luaPrint(...)
    for index, value ipairs {...} do
        print(value)
    end
end

luaPrint(1, 2, 3) --> 1 2 3

가변 인자를 table.pack 으로 묶어서 테이블로 저장 가능하다. 

function a(...)
    local packedTable = table.pack(...)
end


'Lua Script' 카테고리의 다른 글

[Lua] 반복자  (0) 2018.03.28
[Lua] 함수의 내부  (0) 2018.03.28
[Lua] 문장  (0) 2018.03.27
[Lua] 표현식  (0) 2018.03.27
[Lua] 타입과 값  (0) 2018.03.27
Posted by 홍성곤
,

[Lua] 문장

Lua Script 2018. 3. 27. 20:57

1. 제어 구조
1) 조건문

if  a < 0 then 
    a= 0
end

if a < 0 then
    a = 0
elseif a == 0 then
    a = 1
else 
    a = -1
end

위처럼 사용한다. 

2) while문

while true do
    print("while")
end 

3) repeat-until문 
: 몸체를 실행한 다음 조건을 검사하기 때문에, 몸체가 한 번은 꼭 실행된다. 

repeat 
    line = io.read()
until line ~= ""
print(line)

위 코드는 빈 줄이 아닌 첫 줄의 내용을 출력하는 것이다. 즉, until 조건을 만족할때까지 몸체 부분을 계속 수행한다.

4) for문
1- 수치 for문

for var = exp1, exp2, exp3 do
    <something>
end

위 코드는 var의 값을 exp1에서 시작해서 exp2까지 exp3만큼 증가시키면서 <something>을 실행한다. exp3은 생략 가능하다. 생략하는 경우에는 1씩 증가한다. 
제한 없이 반복하고 싶은 경우에는 math.huge 상수를 쓰면 된다. 

2- 일반 for 문

for key, value pairs(t) do 
    print(k, v)
end

위 코드는 테이블 t의 모든 key, value 값을 출력하는 것이다.





'Lua Script' 카테고리의 다른 글

[Lua] 함수의 내부  (0) 2018.03.28
[Lua] 함수  (0) 2018.03.27
[Lua] 표현식  (0) 2018.03.27
[Lua] 타입과 값  (0) 2018.03.27
[Lua] 기본 문법  (0) 2018.03.27
Posted by 홍성곤
,

[Lua] 표현식

Lua Script 2018. 3. 27. 20:14

1. 비교 연산
: 일반적인 언어와 같다. <, >, <=, >=, ==, ~= 을 지원한다. 
테이블과 유저데이터의 동등성은 참조 값(주소 값)이 같은지로 판별한다. 


2. 논리 연산자
: and, or, not이 있다. 
- and : C의 && 
- or : C의 || 
- not : C의 !


3. 테이블 생성자
: 생성자는 테이블을 생성하고 초기화하는 표현식이다. 테이블 생성자는 아주 유용하고 다재다능하게 활용되는 루아의 독특한 기능이다. 

days = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" } 
print(days[4]) --> "Wednesday" 
-- 테이블의 index는 기본적으로 1부터 시작한다. 

a = { "x" = 10, "y" = 20 } 

위처럼 Dictionary와 비슷하게 사용할 수도 있다. 

polyline = {
                   color = "blue",
                   thickness = 2 ,
                   {x = 0, y = 0},   --polyline[1]
                   {x = -10, y = 0} --polyline[2]
                 }

print(polyline[2].x) --> -10

위처럼 배열형식과 Dictionary 형식을 함께 사용해서 초기화할 수 있으며 테이블안에 테이블을 넣는것도 가능하다. 

b = { x = 10, y = 45; "one", "two", "three" }

위와같이 "," 와 ";" 둘 모두 구분자로 사용 가능하다. 


'Lua Script' 카테고리의 다른 글

[Lua] 함수의 내부  (0) 2018.03.28
[Lua] 함수  (0) 2018.03.27
[Lua] 문장  (0) 2018.03.27
[Lua] 타입과 값  (0) 2018.03.27
[Lua] 기본 문법  (0) 2018.03.27
Posted by 홍성곤
,

[Lua] 타입과 값

Lua Script 2018. 3. 27. 18:46

Introduction
: 루아는 동적 타입 언어다. 루아에서는 모든 값마다 타입이 있으므로, 변수를 선언할 때 타입을 지정하지 않는다. 

8가지 기본타입이 있다. 
- nil, boolean, number, string, userdata, function, thread, table

print(type(print)) --> function

a = print 
-- 이렇게 써도 문제가 없다. function도 하나의 타입이기 때문이다. 


1. 강제 변환
: 루아는 실행 중에 숫자와 문자열 사이의 자동 변환을 지원한다. 

print("10" + 1) --> 11
print("10" + "1") --> 11

반대로 문자열이 필요한 곳에 실수 값이 있으면 실수 값을 문자열로 변환한다.

print(10 ... 20)  --> 1020
-- ".."은 루아에서 문자열을 이어붙이는 연산이다. 

10 == "10" 의 결과값은 거짓이다. 
명시적으로 문자열을 실수로 변환할 때는 tonumber 함수를 쓰면 된다. tonumber는 문자열이 실수를 표기하지 않는 경우 nil을 반환한다. 

실수를 문자열로 변환하려면 tostring 함수를 호출하거나 10 .. "" 이처럼 실수에 빈문자열을 연결하면 된다. 


2. 테이블
: 테이블 타입은 연관 배열의 구현체이다. 여기서 연관 배열은 배열의 인덱스로 실수 이외에도 nil을 제외한 나머지 모든 값을 쓸 수 있는 배열을 말한다. 

테이블은 루아에서 유일한 자료구조이다. 또한 패키지나 객체를 표현하기 위해 테이블을 사용하기도 한다.
예를들어, io.read를 쓸때는 "io 모듈의 read 함수" 라는 의미로 쓴다. 루아에서 이 표현식은 "io 테이블에서 read라는 문자열을 키로 사용해서 찾은 것"을 의미한다. 

루아의 테이블은 객체다. 즉, 프로그램은 테이블에 대한 참조나 포인터만 조작한다. 따라서, 의도하지 않게 객체의 사본이 만들어지거나 새 테이블이 생성되는 경우는 없다. 

a = {} -- 테이블을 생성하고 a로 해당 테이블 참조
k = "x"
a[k] = 10
a[20] = "great"
print(a["x"]) --> 10
k = 20 
print(a[k]) --> great

더이상 테이블을 참조하는 변수가 없어지면 루아의 가비지 컬렉터가 메모리를 회수해간다. 

print(a["y"]) --> nil

위처럼 초기화되지 않은 필드의 값은 nil이 된다. 역시 전역변수와 마찬가지로 필드에 nil을 대입하면 필드가 삭제된다. 

print(a.x) --> 10 
-- a["x"]와 같음

#a 로 a 테이블의 길이를 얻어올 수 있다. 그러나 의도와는 다른값이 나올 수 있다.
예를들어 처음에는 10개 짜리 길이의 테이블이었는데 도중에 5번째 인덱스에 nil을 집어넣어 테이블의 값을 지우고 다시 #a로 길이를 참조하면 그래도 10이다. 

for i, j in pairs(a) do

end

그러나 위와같이 사용하면 a의 5번째 인덱스는 건너뛰고 순회한다. 


3. 함수
: 루아의 함수는 변수에 저장할 수 있고 다른 함수의 인자로 넘길 수 있으며 함수를 반환 받을 수도 있다. 


4. 유저데이터
: 유저데이터 타입을 써서 임의의 C 데이터를 루아 변수에 저장할 수 있다. C로 작성된 응용 프로그램이나 라이브러리에서 정의한 새로운 타입을 표현하기 위해 유저데이터를 사용한다.


'Lua Script' 카테고리의 다른 글

[Lua] 함수의 내부  (0) 2018.03.28
[Lua] 함수  (0) 2018.03.27
[Lua] 문장  (0) 2018.03.27
[Lua] 표현식  (0) 2018.03.27
[Lua] 기본 문법  (0) 2018.03.27
Posted by 홍성곤
,

[Lua] 기본 문법

Lua Script 2018. 3. 27. 18:19

1. 청크
: 루아가 실행하는 코드조각
: 청크는 "Hello World" 처럼 한줄로 간단할 수도 있고, ".lua" 파일로 묶어서 파일단위로 청크가 되기도 한다. 

독립적인 인터프리터가 컴퓨터에 설치되어 있다면 커멘드창에서

$ lua 파일이름.lua

이런식으로 실행시킬수 있다. 

$ lua -i 파일이름.lua 

위 명령은 해당 파일에 있는 코드를 실행한 후 대화모드로 넘어간다.

그냥 lua만 입력하면 바로 대화모드로 들어간다. 여기서 lua 코드를 입력하면 바로 실행 결과값이 나타난다. 

대화 모드에서 다른 .lua 파일을 불러올 수 있다.

> dofile("파일이름.lua")
n = norm(1.0, 1.5)  -- 위에서 불러온 파일에 있는 함수 호출

* 대화 모드에서는 local을 이용해서 지역변수를 사용하지 말자. 대화 모드에서는 한줄 한줄이 각기 다른 청크이기 때문에 이전에 선언한 local 변수가 바로 다음줄에서 유효하지 않다. 


2. 주석
한줄 주석 : -- 나는 주석이다.

여러줄 주석 :
-- [[ 
나는 주석
]] -- 


3. 전역 변수 
: 전역 변수는 선언할 필요가 없다. 그냥 쓰면 된다. 선언되지 않은 변수를 사용하면 그냥 nil이 들어갈 뿐이다. 

print(b) --> nil
b = 10
print(b) --> 10 

b = nil 
-- 이렇게 변수에 nil을 대입하면 그 변수에서 사용했던 메모리를 재활용할 수 있게 된다. 



'Lua Script' 카테고리의 다른 글

[Lua] 함수의 내부  (0) 2018.03.28
[Lua] 함수  (0) 2018.03.27
[Lua] 문장  (0) 2018.03.27
[Lua] 표현식  (0) 2018.03.27
[Lua] 타입과 값  (0) 2018.03.27
Posted by 홍성곤
,

[C++] 템플릿(Template)(2)

C++ 2017. 12. 25. 22:27

1] 템플릿 개념의 확장
1. 클래스 템플릿의 확장

template <class T>
class Point
{
private:
    T xpos, ypos;
public:
    Point(T x=0, T y=0);
    void ShowPosition() const;
};

template <class T>
class BoundCheckArray
{
private:
    T *arr;
public:
    BoundCheckArray(T); 
};

위 클래스 기반으로 Point<int> 템플릿 클래스의 객체를 저장할 수 있는 객체는 어떻게 생성해야할까? 딱히 어렵지 않아 보인다.

BoundCheckArray<Point<int>> pointArr(50);

Point<Int>형 포인터라면, 다음과 같이 객체를 생성하면 된다.

BoundCheckArray<Point<int>*> pointArrPtr(50);

타입이 너무 길다면, typedef 선언으로 간략화 할 수 있다.

typedef Point<int>* POINT_PTR;

* 특정 템플릿 클래스의 객체를 인자로 받는 일반함수의 정의와 friend 선언
: Point<int>와 같은 템플릿 클래스의 자료형을 대상으로도 일반함수의 정의가 가능하고, 클래스 템플릿 내에서 이러한 함수를 대상으로 friend 선언도 가능하다. 


2] 클래스 템플릿의 특수화
: 클래스 템플릿의 특수화도 함수 템플릿의 특수화랑 매우 비슷하다.

1. 클래스 템플릿 특수화
: 클래스 템플릿을 특수화하는 이유는, 특정 자료형을 기반으로 생성된 객체에 대해, 구분이 되는 다른 행동양식을 적용하기 위해서이다.

template <class T>
class SoSimple
{
public:
    T SimpleFunc(T num)
    {
        ...
    }
};

위 클래스의 int형 특수화는 다음과 같다.

template<>
class SoSimple<int>
{
public:
    int SimpleFunc(int num)
    {
        ...
    }
};

2. 클래스 템플릿의 부분 특수화

template <class T1, class T2>
class MySimple { ... };

다음은 위 클래스 템플릿을 부분 특수화한 코드이다.

template <class T1>
class MySimple<T1, int> { ... }

그렇다. 두개의 템플릿 타입에 대하여 하나의 타입만 특수화를 한것이다. 

template <>
class MySimple<double, int> { ... }

위는 전체 타입에 대해 특수화를 시킨것이다. 만약 두개의 정의가 겹치는 즉 MySimple<double, int> 타입의 객체를 생성하려고 하면 전체 특수화에 대한 클래스 템플릿이 우선시 된다. 


3] 템플릿 인자
: 위에서 사용된 T 또는 T1, T2 같은 문자를 가리켜 "템플릿 매개변수" 라고 한다. 그리고 템플릿 매개변수에 전달되는 자료형 정보를 가리켜 "템플릿 인자" 라고 한다. 

1. 템플릿 매개변수에는 변수의 선언이 올 수 있다. 
: 다음의 클래스 템플릿 정의를 보자. 이 정의에서 독특한 사실은 템플릿 매개변수의 선언에 마치 함수처럼 변수의 선언이 등장했다는 점이다.

template <typename T, int len> 
class SimpleArray
{
private:
    T arr[len];
public:
    T& operator[] (int idx)
    {
        return arr[idx];
    }
};

이렇듯 템플릿 매개변수에도 변수가 올 수 있다. 그리고 이를 기반으로 다음의 형태로 객체생성이 가능하다.

SimpleArray<int, 5> i5arr;
SimpleArray<double, 7> d7arr;

위의 두 문장에서 템플릿 매개변수 len에 전달된 인자 5와 7은 해당 템플릿 클래스에서 상수처럼 사용된다. 즉, len은 각각 5와 7로 치환되어 템플릿 클래스가 각각 생성된다.

class SimpleArray<int, 5>
{
private:
    int arr[5];
public:
    int& operator[] (int idx)
    {
        return arr[idx];
    }
};

class SimpleArray<double, 7>
{
private:
    int arr[7];
public:
    double& operator[] (int idx)
    {
        return arr[idx];
    }
};

물론, 위의 두 템플릿 클래스 SimpleArray<int, 5>와 SimpleArray<double, 7>은 완전히 다른 타입이다. 

위에서 보인 것처럼 배열의 길이를 결정하기 위해서 굳이 위에처럼 하지말고 생성자를 이용해서 하면 더욱 편리할 것이다. 
그런데 굳이 이러한 템플릿 클래스를 만드는 이유는, 길이가 다른 두 배열 객체간의 대입 및 복사에 대한 부분을 신경 쓰지 않아도 된다는 것이다. 서로 다른 타입이기 때문에 길이가 다른 배열에 객체에 대해 대입 및 복사 연산을 할 경우 컴파일 에러가 발생할 것이기 때문이다. 
만약 생성자를 이용해서 배열의 길이를 결정하게 했다면, 길이가 같은 배열에 대해서만 대입을 허용하기 위해서 추가적인 코드의 삽입이 불가피하며, 이러한 추가적인 코드는 대입 및 복사의 과정에서 CPU가 수행해야 할 일을 늘리는 결과로 이어진다. 

2. 템플릿 매개변수는 디폴트 값 지정도 가능하다.
: 템플릿 매개변수에도 디폴트 값의 지정이 가능하다. 

template <class T=int, int len=7> 
class SimpleArray
{
    ....
}

SimpleArray <> arr;


4] 템플릿과 static
1. 함수 템플릿과 static 지역변수

template <class T>
void ShowStaticVlaue(void)
{
    static T num = 0;
    num += 1;
    cout << num << endl;
}

위의 함수 템플릿은 다음과 같은 템플릿 함수를 만들어 내며 각각의 함수가 static 변수를 따로 가지고 있다. 

void ShowStaticVlaue<int>(void)
{
    static int num = 0;
    num += 1;
    cout << num << endl;
}

void ShowStaticVlaue<long>(void)
{
    static long num = 0;
    num += 1;
    cout << num << endl;
}

2. 클래스 템플릿과 static 멤버변수

template <class T>
class SimpleStaticMem
{
private:
    static T mem;
public:
    void  AddMem(int num)
    {
        mem += num;
    }

    void ShowMem()
    {
        cout<<mem<<endl;
    }
};

template <class T>
T SimpleStaticMem<T>::mem = 0; // 템플릿 기반의 static 멤버 초기화 문장이다.

위 클래스 템플릿도 타입 별 static 멤버 변수가 각각 유지된다.

3. 템플릿 static 멤버변수 초기화의 특수화
위에서 다음과 같이 static 멤버변수를 초기화 하였다.

template <class T>
T SimpleStaticMem<T>::mem = 0;

그러면 long 타입만 5로 초기화하고 싶을때는 어떻게 할까? 간단하다.

template <>
long SimpleStaticMem<long>::mem = 5;


'C++' 카테고리의 다른 글

[C++] 템플릿(Template)(1)  (0) 2017.12.25
[C++] 상속과 다형성  (0) 2017.12.25
[C++] 상속  (0) 2017.12.25
[C++] Friend와 Static 그리고 Const  (0) 2017.12.25
[C++] 복사 생성자(Copy Constructor)  (0) 2017.12.23
Posted by 홍성곤
,