본문 바로가기

C++

c++ 12 (문자열과 네임스페이스 그리고 레퍼런스 타입)

std::string

: 여러가지 문자열 사용의 불편한 점을 해결한 클래스

C++에서 문자열을 활용하기 위한 클래스형 문자열 타입 string이 헤더파일에 정의되어 있다.

std::string에는 문자열을 이용한 여러가지 동작을 할 수 있도록 여러 멤버함수가 정의되어있다

 

또한 문자열의 포인터를 자동으로 관리해주기 때문에 하나하나 포인터를 관리할 필요 없이

정적인 형태의 클래스 인스턴스로 활용할 수 있다.

 

#include <string> 을 선언,

#include <cstring>을 할 필요가 없다

 

std::string은 일반적으로 정적인 형태의 클래스 인스턴스로 선언되고 사용된다.

앞으로 배울 C++에서 제공하는 기본 STL 자료구조의 클래스 역시 일반적으로 정적인 형태

 

정적인 인스턴스로 만드는 이유

1.

std::string* myStrPointer;

이런식으로 동적 클래스를 만들어서 인스턴스를 만들게 되면 사용자가 메모리 관리를 해줘야함

 

2.

=을 통해 const char 포인터를 받는게 가능한 이유는 C++에 연산자 오버로딩 때문

std::string은 +, = 기호가 연산자 오버로딩 되어있다.

=, + 기호에 의해 연산자 오버로딩 된 클래스 인스턴스를 활용하기 위해서는 이렇게

std::string* myStrPointer; 포인터 형태의 주소값에는 연산자 오버로딩을 할 수 가 없다.

 

 

 

 

원래 쓰던 방식

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <cstring>

int main () {
    
    char myString[100];
    strcpy(myString, "Hello World"); // 원래 쓰던 방식
    strcat(myString, "  Hello C++"); // myString에 문자열을 이어붙이고 싶은 경우
    
    printf("%s", myString);
       
    return 0;
}
 

 

 

 

std::string::c_str()

const char*를 리턴해주는 문자열 클래스의 멤버 함수

이를 활용해서 C스타일 문자열의 const char*을 받아올 수 있다.

 

printf를 활용하기 위해서는 이 멤버함수의 호출이 필요하다

 

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <string>
#include <iostream> //std::cout 을 사용시 필요




int main () {
    
    std::string myStr = "Hello World";
    // std::string은 = 기호를 이용해 배열, const char, char 포인터 형태의 문자열 할당 가능

    myStr = myStr + " Hello C++";
    // + 기호를 이용해 문자열에 이어붙이기 연산을 지원

    
    // printf("%s", myStr); 오류
    // %s는 클래스를 int 형태로 강제 형변환을 하고 강제 형변환된 숫자를 주소값으로 인식하고
       거기서 부터 null 문자가 나올 때까지 그 주소에 가서 쭉 문자를 출력



    // 첫번째 출력 방법
    printf("%s", myStr.c_str());
    // myStr이라고 하는 std::string 클래스 안에 있는 실제로 우리가 사용하는 char 포인터 형태의 배열 포인터가 들어오게 된다.
    // . 을 넣는 이유는 정적 인스턴스 클래스이기 때문에 -> 가 이닌 .을 지원
    
    
    
    // 두번째 출력 방법
    std::cout << myStr;
    // std::cout은 C++에서 printf를 대체하기 위한 어떤 정적인 클래스 <<도 연산자 오버로딩 되어있다.
    // 단, 다른 프로그래밍 출력과 이질적 그래서 printf를 활용
    
    return 0;
}
 

 

 

 

std::string::compare( ... )

: strcmp 라는 함수와 동일한 일을 하는 멤버 함수

현재 문자열 클래스의 문자열과 파라미터로 입력받은 문자열을 비교하여

같다면 0,

현재 문자열 클래스가 우선순위가 높다면 1,

파라미터로 받은 문자열 우선순위가 높다면 2를 리턴

 

 

간단한 문자열 비교

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <string>
#include <iostream>




int main () {
 
    std::string str1 = "apple";
    std::string str2 = "banana";
    
    // 두개의 문자열이 같은지 다른지 판단하기 위해선 기존에는 strcmp를 활용했지만
    // std::string의 경우에는 멤버함수로 compare 라는 함수가 있다.
    if(str1.compare(str2) == 0) {
        // str1 이 str2보다 우선순위가 높다면 -1, str2가 높다면 1
        printf("두 문자열은 같습니다.");
    }
    else {
        printf("두 문자열은 다릅니다.");
    }


    
    return 0;
}
 

 

strcmp를 사용하고 싶을땐

if (strcmp(str1.c_str(), str2.c_str()) == 0 ) { }으로도 가능

 

 

 

namespace

프로그래머끼리 같은 이름을 사용하지 않도록

이름들을 구분해주는 역할을 하는 개념

 

이 namespace는 JAVA에서는 package로 불리며

C#에서는 동일하게 namespace라고 부른다.

 

std:: 라는 접두어를 붙여주게 되면서 서로 같은 이름의 함수도 다른 이름으로 구분 할 수 있다.

 

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <string>
#include <iostream>


namespace hong {
    void foo(); // namespace안에 함수가 있다 말해주기
}


namespace kim {
    void foo();
}


int main () {
 
    hong::foo();
    kim::foo();
    
    return 0;
}
 
void hong::foo() { //namespace를 클래스 명처럼 엮어서 만들어주기
    printf("홍길동이 만든 함수 foo() 입니다.\n");
}


void kim::foo() {
    printf("김말똥이 만든 함수 foo() 입니다.\n");
}

 

 

using namespace std; 상단에 선언하여 코드에 std:: 붙이는 것 생략

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <string>
#include <iostream>


using namespace std;
// 이 코드가 나온 시점 아래부터는 전부 다 std라는 namespace를 생략하겠다는 의미



namespace hong { // namespace가 어떤 함수 등등을 하나의 이름 공간 안에 묶어준다고 생각하면 된다.
    void foo(); // namespace안에 함수가 있다 말해주기


    class Marine {
    public:
        Marine(); // 생성자 선언
        void printInfo(int i, const char* str);
    };
}


int main () {
 
    string myStr = "Hello World";
    // std::가 바로 namespace (standard namespace)

    // using namespace std;를 선언하여 std::string myStr에 std::를 생략
    
    printf("%s", myStr.c_str());
        
    
    return 0;
}
 
void hong::foo() {
    printf("홍길동이 만든 함수 foo() 입니다.\n");
}


hong::Marine::Marine(){
    // hong이라는 namespace에 Marine이라는 클래스에 Marine()이라는 생성자를 의미

    // 생성자 몸통, 생성자는 리턴타입 없다
}


void hong::Marine::printInfo(int i, const char* c) {
    // 몸통
}

 

 

 

 

레퍼런스 (Reference) 타입

파라미터를 받을 때

타입 오른쪽에 &(앰퍼센트) 기호를 이용하게 되면

파라미터를 레퍼런스 타입으로 받을 수 있게 된다.

 

이 레퍼런스 타입은 파라미터로 일어나는 값 복사를 막아주어

실행속도를 빠르게 할 수 있다는 장점이 있다.

 

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <string>
#include <iostream>


/*
void swap(int a, int b) {
    int temp;
    temp = a;
    a = b;
    b = temp;
    이 상태로는 swap이 되지 않는다
}
*/


// 1. 포인터타입 파라미터 이용하기
void swap(int* a, int* b) {
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}


// 2. 포인터타입을 &로 바꾸기 -> &로 받으면 포인터 타입으로 바꾸지않아도 된다.
void swap2(int &a, int &b) { // int &a을 파라미터 레퍼런스 타입이라고 한다. 변수 타입 오른쪽에 &붙이면 레퍼런스 타입
    int temp;
    temp = a;
    a = b;
    b = temp;
}




int main () {
 
    int value1 = 100;
    int value2 = 200;
    
    swap(&value1, &value2); // 이 포인터 타입에다 주소값을 넣기 위해서 &을 붙여준다
    swap2(value1, value2);
    
    printf("value1 : %d\n", value1);
    printf("value2 : %d\n", value2);
    
    return 0;
}
 

 

 

 

포인터타입을 &로 바꾸어 레퍼런스 타입을 쓰는 이유 : 문자열 같은 경우는 포인터타입을 이용할 수 없다.

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <string>
#include <iostream>




void swap(std::string& a, std::string& b) {
    std::string temp;
    temp = a;
    a = b;
    b = temp;
}



int main () {
 
    std::string value1 = "Hello";
    std::string value2 = "World";


    swap(value1, value2);
    
    printf("value1 : %s\n", value1.c_str());
    printf("value2 : %s\n", value2.c_str());
    
    return 0;
}
 

 

 

void swap(std::string& a, std::string& b) 장점

1. 앞에 별표를 붙일 필요 없이 그냥 쓸 수 있기 때문

2. 값복사가 최소한도로 일어나게 된다.