1. C++ 자료형
1) auto
auto는 컴파일러가 자료형을 자동으로 추론하게 합니다.
auto count = 10; // int
auto speed = 1.5; // double
auto name = "robot"; // const char*
다만 초보 단계에서는 자료형을 명확히 쓰는 것이 좋습니다.
int count = 10;
double speed = 1.5;
std::string name = "robot";
ROS 2 코드에서는 반복자, 람다, 복잡한 타입을 다룰 때 auto를 자주 사용합니다.

2. 비교 연산자
조건문에서 자주 사용하는 비교 연산자입니다.
| == | 같다 |
| != | 같지 않다 |
| > | 크다 |
| < | 작다 |
| >= | 크거나 같다 |
| <= | 작거나 같다 |
예:
int speed = 10;
if (speed == 0) {
std::cout << "정지 상태" << std::endl;
}
if (speed > 0) {
std::cout << "이동 중" << std::endl;
}
주의할 점이 있습니다.
if (speed = 0) {
}
위 코드는 비교가 아니라 대입입니다.
비교할 때는 반드시 ==를 사용해야 합니다.
if (speed == 0) {
}
3. 논리 연산자
여러 조건을 조합할 때 사용합니다.
연산자 의미
| && | 그리고 |
| || | 또는 |
| ! | 아니다 |
예:
bool gps_ok = true;
bool battery_ok = true;
if (gps_ok && battery_ok) {
std::cout << "비행 가능" << std::endl;
}
bool obstacle_detected = true;
bool emergency_stop = false;
if (obstacle_detected || emergency_stop) {
std::cout << "정지" << std::endl;
}
bool is_connected = false;
if (!is_connected) {
std::cout << "연결되지 않음" << std::endl;
}

4. switch 문
하나의 값에 따라 여러 동작을 나눌 때 사용합니다.
int mode = 2;
switch (mode) {
case 0:
std::cout << "대기 모드" << std::endl;
break;
case 1:
std::cout << "수동 모드" << std::endl;
break;
case 2:
std::cout << "자동 모드" << std::endl;
break;
default:
std::cout << "알 수 없는 모드" << std::endl;
break;
}
break를 쓰지 않으면 아래 case까지 계속 실행될 수 있으므로 주의해야 합니다.

5. 배열 또는 Vector 반복
std::vector<int> sensor_values = {10, 20, 30, 40};
for (int i = 0; i < sensor_values.size(); i++) {
std::cout << sensor_values[i] << std::endl;
}
다른 방식:
for (int value : sensor_values) {
std::cout << value << std::endl;
}

6. break와 continue
1) break
반복문을 즉시 종료합니다.
for (int i = 0; i < 10; i++) {
if (i == 5) {
break;
}
std::cout << i << std::endl;
}
2) continue
현재 반복을 건너뛰고 다음 반복으로 넘어갑니다.
for (int i = 0; i < 10; i++) {
if (i == 5) {
continue;
}
std::cout << i << std::endl;
}

7. C++ 함수 작성
함수는 특정 기능을 하나로 묶은 코드입니다.
1) 함수의 기본 구조
반환자료형 함수이름(매개변수) {
실행할 코드
return 반환값;
}
예:
int add(int a, int b) {
return a + b;
}
사용:
int result = add(3, 5);
std::cout << result << std::endl;
2) 반환값이 없는 함수 void
값을 반환하지 않는 함수는 void를 사용합니다.
void printRobotStatus() {
std::cout << "로봇 상태 출력" << std::endl;
}
사용:
printRobotStatus();
3) 매개변수가 있는 함수
void setMotorSpeed(int left_pwm, int right_pwm) {
std::cout << "왼쪽 모터 PWM: " << left_pwm << std::endl;
std::cout << "오른쪽 모터 PWM: " << right_pwm << std::endl;
}
사용:
setMotorSpeed(120, 130);
4) 반환값이 있는 함수
double calculateDistance(double x1, double y1, double x2, double y2) {
double dx = x2 - x1;
double dy = y2 - y1;
return std::sqrt(dx * dx + dy * dy);
}
사용하려면 <cmath>가 필요합니다.
#include <cmath>
전체 예:
#include <iostream>
#include <cmath>
double calculateDistance(double x1, double y1, double x2, double y2) {
double dx = x2 - x1;
double dy = y2 - y1;
return std::sqrt(dx * dx + dy * dy);
}
int main() {
double distance = calculateDistance(0.0, 0.0, 3.0, 4.0);
std::cout << "거리: " << distance << std::endl;
return 0;
}

5) 로봇 예제 함수
bool isObstacleTooClose(double distance) {
if (distance < 0.5) {
return true;
} else {
return false;
}
}
더 간단하게 쓰면:
bool isObstacleTooClose(double distance) {
return distance < 0.5;
}
사용:
double lidar_distance = 0.3;
if (isObstacleTooClose(lidar_distance)) {
std::cout << "장애물이 너무 가깝습니다. 정지합니다." << std::endl;
}

8. C++ Class 기본
ROS 2 C++ 노드는 대부분 클래스로 작성합니다.
예를 들어 ROS 2에서 자주 보는 구조는 다음과 같습니다.
class MyNode : public rclcpp::Node
{
public:
MyNode() : Node("my_node")
{
}
};
따라서 C++ 클래스를 이해하는 것이 매우 중요합니다.
1) 클래스란?
클래스는 변수와 함수를 하나로 묶은 사용자 정의 자료형입니다.
예를 들어 로봇을 클래스로 표현하면 다음과 같습니다.
class Robot {
public:
std::string name;
double speed;
void move() {
std::cout << name << " 이동 중" << std::endl;
}
void stop() {
std::cout << name << " 정지" << std::endl;
}
};
사용:
Robot robot1;
robot1.name = "delivery_robot";
robot1.speed = 1.0;
robot1.move();
robot1.stop();
2) 객체란?
클래스로 만든 실제 변수를 객체라고 합니다.
Robot robot1;
Robot robot2;
여기서 Robot은 클래스이고, robot1, robot2는 객체입니다.
3) 멤버 변수와 멤버 함수
클래스 안에 있는 변수를 멤버 변수라고 합니다.
std::string name;
double speed;
클래스 안에 있는 함수를 멤버 함수라고 합니다.
void move()
void stop()
4) 접근 지정자 public, private
C++ 클래스에서는 외부에서 접근 가능한 부분과 접근 불가능한 부분을 구분합니다.
| public | 클래스 외부에서 접근 가능 |
| private | 클래스 내부에서만 접근 가능 |
| protected | 상속 관계에서 사용 |
5) private 사용 예
class Robot {
private:
double speed;
public:
void setSpeed(double new_speed) {
speed = new_speed;
}
double getSpeed() {
return speed;
}
};
사용:
Robot robot;
robot.setSpeed(1.5);
std::cout << robot.getSpeed() << std::endl;
이렇게 하면 speed 값을 직접 바꾸지 못하고, setSpeed() 함수를 통해서만 바꾸게 만들 수 있습니다.
좋은 코드에서는 중요한 데이터는 보통 private으로 숨기고, 필요한 함수만 public으로 제공합니다.
6) 생성자
생성자는 객체가 만들어질 때 자동으로 실행되는 함수입니다.
class Robot {
public:
Robot() {
std::cout << "Robot 객체 생성" << std::endl;
}
};
사용:
Robot robot;
객체가 생성되면 자동으로 생성자가 호출됩니다.
7) 매개변수가 있는 생성자
class Robot {
private:
std::string name;
double speed;
public:
Robot(std::string robot_name, double robot_speed) {
name = robot_name;
speed = robot_speed;
}
void printInfo() {
std::cout << "이름: " << name << std::endl;
std::cout << "속도: " << speed << std::endl;
}
};
사용:
Robot robot("delivery_robot", 1.5);
robot.printInfo();

8) 초기화 리스트
C++에서는 생성자에서 멤버 변수를 초기화할 때 초기화 리스트를 많이 사용합니다.
class Robot {
private:
std::string name;
double speed;
public:
Robot(std::string robot_name, double robot_speed)
: name(robot_name), speed(robot_speed)
{
}
void printInfo() {
std::cout << "이름: " << name << std::endl;
std::cout << "속도: " << speed << std::endl;
}
};
ROS 2 노드 생성자에서도 이 문법을 자주 봅니다.
MyNode() : Node("my_node")
{
}
9) ROS 2 노드와 클래스 구조
간단한 ROS 2 C++ 노드 구조는 다음과 같습니다.
#include "rclcpp/rclcpp.hpp"
class SimpleNode : public rclcpp::Node
{
public:
SimpleNode() : Node("simple_node")
{
RCLCPP_INFO(this->get_logger(), "Simple node started");
}
};
int main(int argc, char ** argv)
{
rclcpp::init(argc, argv);
auto node = std::make_shared<SimpleNode>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
이 코드를 이해하려면 최소한 다음 개념이 필요합니다.
| class SimpleNode | SimpleNode 클래스 선언 |
| public rclcpp::Node | ROS 2 Node 클래스를 상속 |
| SimpleNode() | 생성자 |
| : Node("simple_node") | 부모 클래스 생성자 호출 |
| std::make_shared<SimpleNode>() | 객체 생성 |
| rclcpp::spin(node) | 노드 실행 |
9. C++ Header / Source 분리
프로그램이 커지면 모든 코드를 하나의 .cpp 파일에 넣으면 관리가 어렵습니다.
그래서 C++에서는 보통 다음처럼 파일을 나눕니다.
robot.hpp → 클래스 선언
robot.cpp → 함수 구현
main.cpp → 프로그램 실행
ROS 2 패키지에서도 이 구조를 자주 사용합니다.
1) 헤더 파일이란?
헤더 파일은 클래스, 함수, 변수의 선언을 적는 파일입니다.
보통 확장자는 다음을 사용합니다.
.hpp
.h
C++에서는 .hpp를 많이 사용합니다.
2) 소스 파일이란?
소스 파일은 실제 동작 코드를 작성하는 파일입니다.
확장자는 다음을 사용합니다.
.cpp
3) 예제 구조
cpp_basic_example/
├── include/
│ └── robot.hpp
├── src/
│ ├── robot.cpp
│ └── main.cpp
4) robot.hpp
#ifndef ROBOT_HPP
#define ROBOT_HPP
#include <string>
class Robot {
private:
std::string name_;
double speed_;
public:
Robot(const std::string& name, double speed);
void move();
void stop();
void setSpeed(double speed);
double getSpeed() const;
};
#endif
5) 헤더 가드
#ifndef ROBOT_HPP
#define ROBOT_HPP
// 내용
#endif
이 구조를 헤더 가드라고 합니다.
헤더 파일이 여러 번 포함되어 생기는 중복 정의 문제를 막습니다.
현대 C++에서는 다음 방식도 많이 씁니다.
#pragma once
예:
#pragma once
#include <string>
class Robot {
};
둘 다 사용 가능합니다.
6) robot.cpp
#include "robot.hpp"
#include <iostream>
Robot::Robot(const std::string& name, double speed)
: name_(name), speed_(speed)
{
}
void Robot::move() {
std::cout << name_ << " 이동 중, 속도: " << speed_ << std::endl;
}
void Robot::stop() {
std::cout << name_ << " 정지" << std::endl;
}
void Robot::setSpeed(double speed) {
speed_ = speed;
}
double Robot::getSpeed() const {
return speed_;
}
여기서 중요한 문법은 다음입니다.
Robot::move()
Robot 클래스에 속한 move() 함수라는 뜻입니다.
7) main.cpp
#include "robot.hpp"
int main() {
Robot robot("delivery_robot", 1.0);
robot.move();
robot.setSpeed(2.0);
robot.move();
robot.stop();
return 0;
}

8) 왜 Header / Source를 분리하는가?
파일을 분리하는 이유는 명확합니다.
| 코드 관리가 쉬움 | 클래스별로 파일을 나눌 수 있음 |
| 재사용성 증가 | 다른 코드에서 헤더만 포함해서 사용 가능 |
| 컴파일 구조 명확 | 선언과 구현을 분리 가능 |
| 협업에 유리 | 여러 사람이 파일 단위로 작업 가능 |
| ROS 2 패키지 구조와 잘 맞음 | include, src 구조 사용 |
ROS 2 C++ 패키지에서는 보통 다음 구조를 사용합니다.
my_robot_package/
├── include/
│ └── my_robot_package/
│ └── robot_controller.hpp
├── src/
│ ├── robot_controller.cpp
│ └── main.cpp
├── CMakeLists.txt
└── package.xml
10. C++ Vector와 기본 자료구조
ROS 2에서는 여러 개의 데이터를 다루는 일이 많습니다.
예를 들어 다음과 같은 데이터가 있습니다.
라이다 거리 데이터
웨이포인트 목록
모터 속도 목록
센서 측정값 배열
경로 좌표 리스트
이런 데이터를 저장할 때 가장 많이 쓰는 것이 std::vector입니다.
1) std::vector란?
std::vector는 크기가 자동으로 변하는 배열입니다.
배열은 보통 크기가 고정되어 있습니다.
int arr[3] = {10, 20, 30};
하지만 vector는 데이터를 추가하면 크기가 자동으로 늘어납니다.
std::vector<int> numbers;
numbers.push_back(10);
numbers.push_back(20);
numbers.push_back(30);
2) Vector 사용 준비
vector를 사용하려면 헤더를 포함해야 합니다.
#include <vector>
예:
#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers = {10, 20, 30};
for (int num : numbers) {
std::cout << num << std::endl;
}
return 0;
}
3) Vector 선언
std::vector<int> numbers;
std::vector<double> distances;
std::vector<std::string> names;
로봇 예시:
std::vector<double> lidar_ranges;
std::vector<double> waypoint_x;
std::vector<double> waypoint_y;
std::vector<int> motor_pwm_values;
4) 초기값 넣기
std::vector<int> numbers = {1, 2, 3, 4, 5};
std::vector<double> speeds = {0.5, 1.0, 1.5};
std::vector<std::string> frames = {"map", "odom", "base_link"};
5) 데이터 추가 push_back
std::vector<int> numbers;
numbers.push_back(10);
numbers.push_back(20);
numbers.push_back(30);
로봇 예시:
std::vector<double> waypoints;
waypoints.push_back(1.0);
waypoints.push_back(2.0);
waypoints.push_back(3.0);
6) 데이터 접근
std::vector<int> numbers = {10, 20, 30};
std::cout << numbers[0] << std::endl;
std::cout << numbers[1] << std::endl;
std::cout << numbers[2] << std::endl;
주의할 점:
C++ 인덱스는 0부터 시작합니다.
numbers[0] → 첫 번째 값
numbers[1] → 두 번째 값
numbers[2] → 세 번째 값
7) Vector 크기 확인 size()
std::vector<int> numbers = {10, 20, 30};
std::cout << numbers.size() << std::endl;
반복문에서 자주 사용합니다.
for (int i = 0; i < numbers.size(); i++) {
std::cout << numbers[i] << std::endl;
}
다만 numbers.size()는 size_t 타입이므로 엄밀하게는 다음처럼 쓰는 것이 좋습니다.
for (std::size_t i = 0; i < numbers.size(); i++) {
std::cout << numbers[i] << std::endl;
}
8) 범위 기반 for문
가장 깔끔한 방식입니다.
std::vector<int> numbers = {10, 20, 30};
for (int number : numbers) {
std::cout << number << std::endl;
}
값을 수정하려면 참조를 사용합니다.
for (int& number : numbers) {
number = number * 2;
}
복사 비용을 줄이려면 const 참조를 사용합니다.
for (const std::string& name : names) {
std::cout << name << std::endl;
}
std::string은 내부적으로 문자열 데이터를 가지고 있기 때문에, 길이가 긴 문자열이면 복사 비용이 생깁니다.
반면에 아래처럼 쓰면:
const std::string& name
name은 원본 문자열을 복사하지 않고 참조합니다. 즉, names 안에 있는 실제 문자열을 그대로 가리킵니다.
여기서 const는 값을 수정하지 못하게 막는 역할입니다.
9) 데이터 삭제
마지막 값을 삭제할 때는 pop_back()을 사용합니다.
std::vector<int> numbers = {10, 20, 30};
numbers.pop_back();
전체 삭제:
numbers.clear();
비어 있는지 확인:
if (numbers.empty()) {
std::cout << "비어 있음" << std::endl;
}
10) Vector와 구조체
웨이포인트처럼 여러 값을 하나로 묶어야 할 때는 구조체와 Vector를 같이 사용하면 좋습니다.
struct Waypoint {
double x;
double y;
double z;
};
사용:
std::vector<Waypoint> waypoints;
waypoints.push_back({1.0, 2.0, 0.5});
waypoints.push_back({3.0, 4.0, 0.5});
waypoints.push_back({5.0, 6.0, 0.5});
출력:
for (const Waypoint& wp : waypoints) {
std::cout << "x: " << wp.x
<< ", y: " << wp.y
<< ", z: " << wp.z
<< std::endl;
}
이 구조는 ROS 2 자율주행, 드론 미션, 경로 생성에서 매우 많이 사용됩니다.

11) 기본 자료구조 정리
C++에서 자주 사용하는 기본 자료구조는 다음과 같습니다.
| std::vector | <vector> | 크기가 변하는 배열 |
| std::array | <array> | 크기가 고정된 배열 |
| std::list | <list> | 중간 삽입/삭제에 유리 |
| std::map | <map> | key-value 저장 |
| std::queue | <queue> | FIFO 구조 |
| std::stack | <stack> | LIFO 구조 |
std::map
key와 value를 함께 저장합니다.
#include <map>
#include <string>
std::map<std::string, double> params;
params["max_speed"] = 1.5;
params["min_distance"] = 0.3;
params["wheel_radius"] = 0.05;
사용:
std::cout << params["max_speed"] << std::endl;
ROS 2 파라미터 개념을 이해할 때 도움이 됩니다.

std::queue
먼저 들어온 데이터가 먼저 나가는 구조입니다.
#include <queue>
std::queue<int> commands;
commands.push(1);
commands.push(2);
commands.push(3);
int first = commands.front();
commands.pop();
센서 데이터 처리, 명령 큐, 작업 큐에서 사용할 수 있습니다.

std::stack
나중에 들어온 데이터가 먼저 나가는 구조입니다.
#include <stack>
std::stack<int> history;
history.push(10);
history.push(20);
int last = history.top();
history.pop();
상태 복구, 경로 되돌리기 같은 구조에 응용할 수 있습니다.
11. 전체 예제: 간단한 로봇 클래스
아래 예제는 지금까지 배운 내용을 모두 포함합니다.
#include <iostream>
#include <vector>
#include <string>
#include <cmath>
struct Waypoint {
double x;
double y;
};
class Robot {
private:
std::string name_;
double x_;
double y_;
double speed_;
public:
Robot(const std::string& name)
: name_(name), x_(0.0), y_(0.0), speed_(0.0)
{
}
void setSpeed(double speed) {
speed_ = speed;
}
void moveTo(double target_x, double target_y) {
double distance = calculateDistance(target_x, target_y);
std::cout << name_ << " 목표 지점으로 이동" << std::endl;
std::cout << "목표 x: " << target_x << ", y: " << target_y << std::endl;
std::cout << "거리: " << distance << std::endl;
x_ = target_x;
y_ = target_y;
}
double calculateDistance(double target_x, double target_y) {
double dx = target_x - x_;
double dy = target_y - y_;
return std::sqrt(dx * dx + dy * dy);
}
void printStatus() const {
std::cout << "로봇 이름: " << name_ << std::endl;
std::cout << "현재 위치: " << x_ << ", " << y_ << std::endl;
std::cout << "속도: " << speed_ << std::endl;
}
};
int main() {
Robot robot("delivery_robot");
robot.setSpeed(1.0);
std::vector<Waypoint> waypoints = {
{1.0, 1.0},
{2.0, 3.0},
{4.0, 5.0}
};
for (const Waypoint& wp : waypoints) {
robot.moveTo(wp.x, wp.y);
robot.printStatus();
}
return 0;
}

'강좌 > ROS2' 카테고리의 다른 글
| ROS 2 package.xml 이해하기 : Python 패키지와 C++ 패키지 기준 (0) | 2026.05.24 |
|---|---|
| C++ ROS 2 패키지 생성하기 (0) | 2026.05.24 |
| VS Code 원격 개발 환경 (0) | 2026.05.24 |
| ROS 2 Humble rqt Plugins 정리 #3 (0) | 2026.05.24 |
| 로봇 개발자를 위한 Python 기초 교육 #2 (0) | 2026.05.24 |