본문 바로가기

명품 C++

[명품 C++] 5. 함수와 참조, 복사 생성자

5.1 함수의 인자 전달 방식 리뷰 

- 값에 의한 호출 : 호출하는 코드에서 넘겨주는 실인자 값이 함수의 매개 변수에 복사되어 전달 

- 주소에 의한 호출 : 주소를 직접 포인터 타입의 매개 변수에 전달 받음

 

값에 의한 호출로 swap 사용 

- m,n 값에 변화가 존재하지 않음

#include <iostream>
using namespace std;

void swap(int a, int b) {
	int tmp;
    
    tmp = a;
    a=b;
    b = tmp;
}

int main(){
	int m=2, n=9;
    swap(m,n);
    cout << m << ' '<< n;
}

주소에 의한 호출로 swap 함수 호출 

- m과 n 값이 변경됨

#include <iostream>
using namespace std;

void swap(int *a, int *b) {
	int tmp;
    
    tmp = *a;
    *a=*b;
    *b = tmp;
}

int main(){
	int m=2, n=9;
    swap(&m,&n);
    cout << m << ' '<< n;
}

 

 

 

5.2 함수 호출시 객체 전달

값에 의한 호출 과정

- 값에 의한 호출 과정은 매개 변수를 직접 복사하므로 increase(waffle) 값은 waffle 값을 복사하여 별개의 스택에 저장하고, 함수가 종료하면 객체가 소멸한다. 즉 waffle 변수 자체의 값은 변하지 않는다. 

void increase(Circle c){
	int r = c.getRadius();
    c.setRadius(r+1);
}
int main(){
	Circle waffle(30);
    increase(waffle); // 값에 의한 호출 과정 
    cout << waffle.getRadius() << "\n"; 
    // 30 출력 
}

문제점 : 객체를 매개변수로 가지는 함수의 경우 생성자는 실행되지 않고 소멸자만 실행되도록 컴파일 하기 때문에 비대칭 구조가 일어난다

 

주소에 의한 호출로 객체를 전달하면 이 문제점을 해결할 수 있다.

원본 객체를 복사하는 시간 소모가 없지만, 원본을 잃어버릴 수 있으므로 유의해야한다

void increase(Circle *p){
	int r = p->getRadius();
    c->setRadius(r+1);
}
int main(){
	Circle waffle(30);
    increase(&waffle); // 주소에 의한 호출
    cout << waffle.getRadius() << "\n"; 
    // 31 출력 
}

 

 

 

5.3 객체 치환 및 객체 리턴

객체 치환 시 객체의 모든 데이터가 비트 단위로 복사된다

동일한 클래스 타입에 대해서만 적용된다

Circle c1(5);
Circle c2(30);

c1=c2; //c2 객체를 c1객체에 비트 단위로 복사한다

함수의 객체 리턴

Circle getCircle(){
	Circle tmp(30);
    return tmp; //객체 tmp 리턴
}

int main(){
	Circle c; // c의 반지름은 1
    c= getCircle(); //tmp 객체의 복사본이 c에 치환됨. c의 반지름이 30
}

 

 

 

5.4 참조와 함수

참조 변수를 선언하기 위해 & 기호를 사용하고 이를 참조자라고 부른다

참조란 이미 선언된 변수에 대한 별명이다

선언 시 반드시 원본 변수로 초기화하여야 한다 

참조 변수의 배열을 만들 수 없다.

int n=2;
int &refn = n; //참조 변수 refn 선언, refn은 n에 대한 별명. refn과 n은 동일한 변수

Circle circle;
Circle &refc = circle; //참조 변수 refc 선언, refc은 circle에 대한 별명. refc와 circle은 동일한 변수
//refn과 refc는 따로 변수 공간을 가지지 않고 각각 n과 circle을 공유한다

//포인터 생성도 가능
int *p = &refn; //p는 refn의 주소를 가짐 
*p = n; //n=20, p=20

참조에 의한 호출

- 함수의 매개 변수를 참조 타입으로 선언하여 매개 변수가 함수를 호출하는 쪽의 실인자를 참조하여 실인자와 공간을 공유하게 함

#include <iostream>
using namespace std;

void swap (int &a, int &b){
    int tmp;
    
    tmp = a;
    a = b;
    b = tmp;
} //참조 매개 변수 a와 b는 이름만 존재하고 공간을 할당받지 않는다

int main (void){
    int m=2, n=9;
    swap(m,n);
    cout << m << ", " << n ;
}

참조 매개 변수가 필요한 경우

#include <iostream>
using namespace std;

bool average (int a[], int size, int &avg){
    if (size<=0) return false;
    int sum = 0;
    for (int i=0;i<size;i++){
        sum += a[i];
    }
    avg = sum/size;
    return true;
}

int main (void){
    int x[] = {0,1,2,3,4,5};
    int avg;
    if(average(x, 6, avg)) cout<<"평균은 " << avg << "\n";
    else cout<<"error\n";
    
    if(average(x, -2, avg)) cout<<"평균은 " << avg << "\n";
    else cout<<"error\n";
    
}

값에 의한 호출로 값을 매개 변수에 전달하면 두 가지 사항에 유의해야 한다

- 함수 내에서 매개 변수 객체를 변경해도 원본 객체를 변경시키지 않는다

- 매개 변수 객체의 생성자가 실행되지 않고 소멸자만 실행되는 비대칭 구조로 작동

그에 반해 참조에 의한 호출은?

- 참조 매개 변수로 이루어진 모든 연산은 원본 객체에 대한 연산이 된다

- 참조 매개 변수는 이름만 생성되므로 생성자와 소멸자는 아예 실행되지 않는다

 

예제 5-6)

#include <iostream>
using namespace std;

class Circle{
    int radius;
public:
    Circle(){
        radius = 1;
        cout << "생성자 실행 radius = " << radius << "\n";
    }
    ~Circle(){
        cout << "소멸자 실행 radius = " << radius << "\n";
    };
    Circle(int radius){
        this->radius=radius;
        cout << "생성자 실행 radius = " << radius << "\n";
    };
    double getArea() {return 3.14*radius*radius;}
    int getRadius() {return radius;}
    void setRadius(int radius){
        this->radius=radius;
    }
};

void increase(Circle &c){
    int r = c.getRadius();
    c.setRadius(r+1);
}

int main (void){
    Circle waffle(30);
    increase(waffle);
    cout << waffle.getRadius() << "\n";
}

예제 5-7)

#include <iostream>
using namespace std;

class Circle{
    int radius;
public:
    Circle(){
        radius = 1;
    }
    Circle(int radius){
        this->radius=radius;
    };
    double getArea() {return 3.14*radius*radius;}
    int getRadius() {return radius;}
    void setRadius(int radius){
        this->radius=radius;
    }
};

void readRadius(Circle &c){
    int r;
    cout << "정수 값으로 반지름을 입력하세요>>";
    cin >> r;
    c.setRadius(r);
}

int main (void){
    Circle donut;
    readRadius(donut);
    cout << "donut의 면적 = "<<donut.getArea() << "\n";
}

참조 리턴

함수가 참조를 리턴할 수 있다

char c = 'a';

char& find(){
    return c; //변수 c에 대한 참조 리턴
}

char a = find(); // a = 'a'
char &ref = find(); //ref는 c에 대한 참조
ref = 'M'; // c = 'M'

find() = 'b'; //c = 'b' 가 됨

예제 5-8)

#include <iostream>
using namespace std;

char& find(char s[], int index){
    return s[index];
}

int main(){
    char name[] = "MIKE";
    cout << name << "\n";
    
    find(name,0) = 'S'; //SIKE
    cout << name << "\n";
    
    char &ref = find(name,2); //ref = 'K'
    ref = 'T'; //SITE
    cout << name << "\n";
}

 

 

 

5.5 복사생성자

얕은 복사 : 충돌 발생 할 수 있음. 포인터가 복사되었기 때문에 메모리를 공유함. 가능하면 일어나지 않게 할 것

깊은 복사 : 포인터의 메모리도 복사함

복사 방법

복사 생성자 선언

- 복사 생성자의 매개 변수는 오작 하나, 자기 클래스에 대한 참조, 클래스에 오직 한 개만 선언할 수 있다

class ClassName {
	ClassName (const ClassName &c); //복사 생성자
}

복사 생성자 실행 

Circle (const Circle& c){
	this->radius = c.radius;
}

Circle src(30);

Circle dest(stc);

예제 5-9)

#include <iostream>
using namespace std;

class Circle{
    int radius;
public:
    Circle(const Circle& c){
        this->radius = c.radius;
        cout << "복사 생성자 실행 radius = " << radius << "\n";
    };
    Circle(){radius=1;}
    Circle(int radius){
        this->radius=radius;
    }
    double getArea() {
        return 3.14*radius*radius;
    }
};


int main(){
    Circle src(30);
    Circle dest(src);
    
    cout << "원본의 면적 = " << src.getArea() << "\n";
    cout << "사본의 면적 = " << dest.getArea() << "\n";
}

복사 생성자 가지지 않았을 때 : 디폴트 복사 생성자를 묵시적으로 삽입하고 이 생성자를 호출하도록 컴파일 함

: 얕은 복사를 실행함

예제 5-11)

#include <iostream>
#include <cstring>
using namespace std;

class Person{
    char* name;
    int id;
public:
    Person(int id, const char* name){
        this->id=id;
        int len = strlen(name);
        this->name=new char[len+1];
        strcpy(this->name, name);
    };
    Person(const Person& person){
        this->id=person.id;
        int len = strlen(person.name);
        this->name = new char [len+1];
        strcpy(this->name, person.name);
        cout << "복사 생성자 실행. 원본 객체의 이름" << this->name << "\n";}
    ~Person(){
        if(name)
            delete [] name;
    };
    void changeName(const char *name){
        if (strlen(name)>strlen(this->name))
            return;
        strcpy(this->name, name);
    }
    void show(){
        cout << id << "," << name << "\n";
    }
};


int main(){
    Person father(1,"kitae");
    Person daughter(father);
    
    cout << "daughter 생성 직후 " << "\n";
    father.show();
    daughter.show();
    
    daughter.changeName("Grace");
    cout << "daughter 이름을 Grace로 변경한 후 " << "\n";
    father.show();
    daughter.show();
}

묵시적 복사 생성

1. 객체로 초기화 하여 객체가 생성될 때

2. 값에 의한 호출로 객체가 전달될 때

3. 함수가 객체를 리턴할 때

 

 

 

실습 문제

1

#include <iostream>
using namespace std;
class Circle{
    int radius;
public:
    Circle() {radius=1;}
    Circle(int r) { this->radius = r; }
    void show(){
        cout << "반지름 : " << radius<<"\n";
    }
};

void swap(Circle &first, Circle &second){
    Circle tmp;
    
    tmp=first;
    first=second;
    second=tmp;
}

int main(){
    Circle a(1), b(3);
    a.show();
    b.show();
    
    swap(a,b);
    a.show();
    b.show();
}

2

#include <iostream>
using namespace std;

void half(double &n){
    n = n/2;
}
int main(){
    double n = 20;
    half(n);
    cout << n;
}

3

#include <iostream>
using namespace std;

void combine(string &one, string &two, string &three){
    three = one+' '+two;
}

int main(){
    string text1("i love you"), text2("very much");
    string text3;
    combine(text1, text2, text3);
    cout << text3;
}

4

#include <iostream>
using namespace std;

bool bigger(int a, int b, int &big){
    if (a==b){
        big = a;
        return true;
    }else{
        if(a > b) big = a;
            else big = b;
        return false;
    }
}

int main(){
    int a, b, big;
    cout << "두 개의 정수를 입력하세요";
    cin >> a >> b;
    
    if (bigger(a, b, big)) {
        cout << "두 수는 같습니다" << endl;
    } else {
    cout << a << "와 " << b << "중 큰 수는 " << big << "입니다." << endl;
}
}

5

#include <iostream>
using namespace std;

class Circle{
    int radius;
public:
    Circle(int r) { radius=r;}
    int getRadius(){return radius;}
    void setRadius(int r){radius=r;}
    void show(){
        cout << "반지름이 " << radius << "인 원" << "\n";
    }
};

void increaseBy(Circle &a, Circle &b){
    int r = a.getRadius() + b.getRadius();
    a.setRadius(r);
}

int main(){
    Circle x(10), y(5);
    increaseBy(x, y);
    x.show();
}

6

#include <iostream>
#include <string>

using namespace std;

char& find(char a[], char c, bool& success) {
    for (int i = 0; i < strlen(a); i++) {
        if (a[i] == c) {
            success = true;
            return a[i];
        }
    }
    success = false;
}

int main() {
    char s[] = "Mike";
    bool b = false;
    char& loc = find(s, 'M', b);
    if (b == false) {
        cout << "M을 발견할 수 없다" << endl;
        return 0;
    }
    loc = 'm';
    cout << s << endl;
    return 0;
}

7

#include <iostream>
#include <string>

using namespace std;

class MyIntStack {
    int p[10];
    int tos;
public:
    MyIntStack(){
        tos = -1;
    };
    bool push(int n){
        if (tos >= 9) return false;
        p[++tos] = n;
        return true;
    };
    bool pop(int& n){
        if (tos < 0) return false;
        n = p[tos--];
        return true;
    };
};

int main() {
    MyIntStack a;
    for (int i = 0; i < 11; i++) {
        if (a.push(i)) cout << i << ' ';
        else cout << endl << i + 1 << " 번째 stack full" << endl;
    }
    int n;
    for (int i = 0; i < 11; i++) {
        if (a.pop(n)) cout << n << ' ';
        else cout << endl << i + 1 << " 번째 stack empty";
    }
    cout << endl;
}

8

#include <iostream>
#include <string>

using namespace std;

class MyIntStack {
    int *p;
    int size;
    int tos;
public:
    MyIntStack() :MyIntStack(1) {}
    MyIntStack(int size){
        this->p = new int[size];
        this->size = size;
        this->tos = -1;
    }
    MyIntStack(MyIntStack& s){
        int len=s.size;
        
        this->p = new int[len];
        this->size=len;
        this->tos = s.tos;
        
        for (int i=0;i<tos;i++){
            this->p[i] = s.p[i];
        }
    }
        bool push(int n){
            if (tos>=9) return false;
            p[++tos] = n;
            return true;
        }

    bool pop(int& n){
        if (tos < 0) return false;
        n = p[tos--];
        return true;
    }
    ~MyIntStack(){
        delete []p;
    }
};

int main() {
    MyIntStack a(10);
        a.push(10);
        a.push(20);
        MyIntStack b = a;
        b.push(30);

        int n;
        a.pop(n);
        cout << "스택 a에서 팝한 값 " << n << endl;

        b.pop(n);
        cout << "스택 b에서 팝한 값 " << n << endl;
}

9

#include <iostream>

using namespace std;

class Accumulator {
    int value;
public:
    Accumulator(int value){
        this->value=value;
    };
    Accumulator& add(int n){
        value += n;
        return *this;
    };
    int get(){
        return value;
    };
};

int main() {
    Accumulator acc(10);
    acc.add(5).add(6).add(7);
    cout << acc.get();
}

10

#include <iostream>
using namespace std;

class Buffer {
    string text;
public:
    Buffer(string text) { this->text = text; }
    void add(string next) { text += next; }
    void print() { cout << text << endl; }
};

Buffer &append (Buffer& buf, string text){
    buf.add(text);
    return buf;
}

int main() {
    Buffer buf("Hello");
    Buffer& temp = append(buf, "Guys");
    temp.print();
    buf.print();

    return 0;
}

11

(1) char

#include <iostream>
#include <string>
#include <cstring>
using namespace std;

class Book {
    char* title;
    int price;
public:
    Book(const char* title, int price){
        int len = strlen(title);
        this->title = new char[len + 1];
        strcpy(this->title, title);
        this->price = price;
    }
    Book(Book& b){
        int len = strlen(b.title);
        this->title = new char [len+1];
        strcpy(this->title, b.title);
        this->price = b.price;
    }
    ~Book(){
        if (this->title)
            delete[]title;
    }
    void set(const char* title, int price) {
        if (this->title) delete[] this->title; // 할당된 메모리가 있다면 반환한후
        int len = strlen(title);
        this->title = new char[len + 1]; // 새로운 메모리 다시 할당
        strcpy(this->title, title);
        this->price = price;
    }
    void show() { cout << title << ' ' << price << "원" << endl; }
};

int main() {
    Book cpp("명품C++", 10000);
    Book java = cpp;
    java.set("명품자바", 12000);
    cpp.show();
    java.show();

}

(2)string

#include <iostream>
#include <string>
#include <cstring>
using namespace std;

class Book {
    string title;
    int price;
public:
    Book(string title, int price){
        this->title = title;
        this->price=price;
    }
    void set(string title, int price) {
        this->title = title;
        this->price = price;
    }
    void show() { cout << title << ' ' << price << "원" << endl; }
};

int main() {
    Book cpp("명품C++", 10000);
    Book java = cpp;
    java.set("명품자바", 12000);
    cpp.show();
    java.show();

}

12

(1)

#include <iostream>
#include <string>
using namespace std;

class Dept {
    int size;
    int* scores;
public:
    Dept(int size) {
        this->size = size;
        scores = new int[size];
    }
    Dept(const Dept& dept){
        this->size = dept.size;
        this->scores = new int[dept.size];
        for (int i = 0;i < this->size;i++)
            scores[i] = dept.scores[i];
    }
    ~Dept() {
        if (this->scores) {
            delete[]scores;
        }
    }
    int getSize() {
        return this->size;
    }
    void read() {
        cout << size << "개 정수 입력 >> ";
        for (int i = 0;i < size;i++)
            cin >> scores[i];
    }
    bool isOver60(int index) {
        if (this->scores[index] > 60) {
            return true;
        }
        else {
            return false;
        }
    }
};
int countPass(Dept dept) {
    int count = 0;
    for (int i = 0;i < dept.getSize();i++)
        if (dept.isOver60(i)) count++;
    return count;
}

int main() {
    Dept com(10);
    com.read();
    int n = countPass(com);
    cout << "60점 이상은 " << n << "명" << endl;
}

(3)

include <iostream>
#include <string>
using namespace std;

class Dept {
    int size;
    int* scores;
public:
    Dept(int size) {
        this->size = size;
        scores = new int[size];
    }
    ~Dept() {
        if (this->scores) {
            delete[]scores;
        }
    }
    int getSize() {
        return this->size;
    }
    void read() {
        cout << size << "개 정수 입력 >> ";
        for (int i = 0;i < size;i++)
            cin >> scores[i];
    }
    bool isOver60(int index) {
        if (this->scores[index] > 60) {
            return true;
        }
        else{
            return false;
        }
    }
};

int countPass(Dept &dept) {
    int count = 0;
    for (int i = 0;i < dept.getSize();i++)
        if (dept.isOver60(i)) count++;
    return count;
}

int main() {
    Dept com(10); 
    com.read();
    int n = countPass(com);
    cout << "60점 이상은 " << n << "명" << endl;
}