হে

হেডার মধ্যে C++

2টি অনুশীলনী

হেডার সম্পর্কে

C++-এ ডিক্লারেশন প্রায়ই ডেফিনিশন থেকে আলাদা রাখা হয়। ডিক্লারেশনগুলো তথাকথিত হেডার ফাইলে সাজানো হয়, আর সংশ্লিষ্ট ইমপ্লিমেন্টেশন রাখা হয় সোর্স ফাইলে। হেডার ফাইলগুলোকে একটি API হিসেবে ভাবতে পারেন। হেডার ফাইল একটি কোডবেস কী অফার করে তা বলে দেবে, কিন্তু কীভাবে তা নিয়ে বিস্তারিত না গিয়ে।

হেডার ও সোর্স

হেডার ফাইলের সবচেয়ে কমন ফাইল এক্সটেনশন হলো .h। কিছু প্রজেক্ট .hpp ব্যবহার করে, আবার কিছু এক্সটেনশন একেবারেই বাদ দেয়।

ডেফিনিশনগুলো থাকে আলাদা একটি .cpp ফাইলে। অংশগুলো আবার একত্র করতে, সোর্স ফাইলটি সংশ্লিষ্ট হেডার ফাইল ইনক্লুড করে শুরু হয়।

আপনি যদি "quick_math" নামের একটি লাইব্রেরি লিখতে চান, যেটি "super_root" নামের এমন একটি ফাংশন দেয় যা আপনি প্রায়ই ব্যবহার করতে চান, তাহলে ফাইলগুলো দেখতে হবে এমন:

// A file named quick_math.h
#pragma once

namespace quick_math {

double super_root(double x, int n);

}
// A file named quick_math.cpp
#include <cmath>

#include "quick_math.h"

double quick_math::super_root(double x, int n) {
    while (n) {
        x = std::sqrt(x), --n;
    }
    return x;
}

যদি এমন কোনো হেডার ইনক্লুড করতে হয় যা কেবল ইমপ্লিমেন্টেশনের জন্যই দরকার, তাহলে সংশ্লিষ্ট #include লাইনটি কেবল সোর্স ফাইলেই লাগে। হেডারে যা যা ইনক্লুড করা হয়, সবকিছুই .cpp ফাইলে পাওয়া যায়, যেমন নিচের উদাহরণে string লাইব্রেরি। লক্ষ্য করুন: হেডার ফাইলে ডিক্লারেশনের পরে ; লাগে, কিন্তু সোর্স ফাইলে ডেফিনিশনের পরে লাগে না।

Note

Exercism-এ অনেক C++ অনুশীলনী দুটি প্রায় খালি ফাইল দিয়ে শুরু হয়: হেডার আর সোর্স। অনুশীলনীটি সমাধান করতে হলে, কোন কোন ফাংশন আর নেমস্পেস প্রত্যাশিত, তা দেখতে আপনাকে *_test.cpp ফাইলটি দেখতে হবে।

ক্লাস ও হেডার

ক্লাস খুব জটিল হয়ে উঠতে পারে, আর হেডার / সোর্স ভাগাভাগির সাথে তাদের সম্পর্ক বিভ্রান্তিকর লাগতে পারে। একটি সম্ভাব্য বিন্যাস হলো সব ইমপ্লিমেন্টেশনের বিস্তারিত সোর্স ফাইলে রাখা, আর সব ডিক্লারেশন ও মেম্বার ভ্যারিয়েবল হেডারে রাখা:

// A file named robot_flower.h
#if !defined(ROBOT_FLOWER_H)
#define ROBOT_FLOWER_H

#include <string>

namespace robots {

class Flower {
   private:
    bool needs_water{};
    int size{};
    std::string name{};

   public:
    Flower(std::string name, int size = 0);
    void give_water();
    std::string get_name();
    int get_size();
    void start_next_day();
};
}  // namespace robots
#endif
// A file named robot_flower.cpp
#include "robot_flower.h"

robots::Flower::Flower(std::string name, int size) {
    this->name = "Robotica " + name;
    this->size = size;
}

void robots::Flower::start_next_day() {
    if (!needs_water) ++size;
    needs_water = true;
}

std::string robots::Flower::get_name() { return name; }

int robots::Flower::get_size() { return size; }

হেডার যখন API-এর ওভারভিউ হিসেবে ব্যবহৃত হয়, তখন ডিফল্ট মানের মতো তথ্যের জন্য মানুষ সেখানেই খোঁজে। তাই কনস্ট্রাক্টরের size প্যারামিটারের ডিফল্ট মান হেডারে সামলানো হয়, ইমপ্লিমেন্টেশনে নয়। সোর্স ফাইলের ডেফিনিশনগুলোর সামনে নেমস্পেস robots আর ক্লাস টাইপ Flower বসানো হয়।

আরেকটি বিন্যাসের বিকল্প হলো হেডার-ওনলি লাইব্রেরি, যার কোনো .cpp ফাইলই থাকে না:

// A file named robot_flower.h
#pragma once

#include <string>

namespace robots {

class Flower {
   private:
    bool needs_water{};
    int size{};
    std::string name{};

   public:
    Flower(std::string name, int size = 0) {
        this->name = "Robotica " + name;
        this->size = size;
    }
    void give_water() { needs_water = false; }
    std::string get_name() { return name; }
    int get_size() { return size; }
    void start_next_day() {
        if (!needs_water) ++size;
        needs_water = true;
    }
};
}  // namespace robots

প্রজেক্টগুলো এসব বিন্যাসের মিশ্রণ ব্যবহার করতে পারে, আর কোন কাজে কোনটি সবচেয়ে ভালো মানায় তা নিয়ে অনেক আলোচনা আছে।

ইনক্লুড গার্ড

উপরের উদাহরণ হেডার ফাইলে #pragma once লাইনটি আপনার নজরে পড়ে থাকতে পারে। এটাকে ইনক্লুড গার্ড বলা হয়, আর এটি নিশ্চিত করে যে কম্পাইলেশনের সময় ফাইলের বিষয়বস্তু কেবল একবারই ইনক্লুড হয়, যাতে এরর না হয়। ইনক্লুড গার্ডের আরেকটি, আরও জটিল রূপ আছে যা #ifndef দিয়ে শুরু হয় আর #endif দিয়ে শেষ হয়; তা নিচে বিস্তারিত বলা হয়েছে।

Advanced

ফরওয়ার্ড ডিক্লারেশন

C++ কোড ধাপে ধাপে মূল্যায়ন করা হয়। কোনো ফাংশন ব্যবহার করতে চাইলে, ব্যবহারের মুহূর্তে সেটি কম্পাইলারের জানা থাকতে হবে। কখনো কখনো সোর্স কোডে ডেফিনিশনগুলো সাজানোর জন্য একটি সরলরৈখিক ক্রম খুঁজে পাওয়া সম্ভব হয় না। নিচের উদাহরণটি দেখুন:

int myFunction(int n) {
    if (n < 10) {
        return n;
    } else {
        return myOtherFunction(n / 10);
    }
}

int myOtherFunction(int m) { return myFunction(m / 2); }

myFunction ডিফাইন করার সময় কম্পাইলার myOtherFunction সম্পর্কে তখনো জানে না। দুর্ভাগ্যবশত, ক্রম বদলে দিয়ে এই চক্রাকার রেফারেন্সের সমস্যাটি সমাধান করা যায় না।

myFunction আর myOtherFunction ডিফাইন হওয়ার আগেই কম্পাইলারকে জানানোর জন্য C++ ফরওয়ার্ড ডিক্লারেশন দেয়। কম্পাইলার ধরে নেয় যে ডিক্লারেশনের পরে কোনো এক সময়ে ডেফিনিশন আসবে। পরের উদাহরণটি দেখায় কীভাবে ফাংশনের জন্য ফরওয়ার্ড ডিক্লারেশন ব্যবহার করা হয়।

int myFunction(int n);       // Forward declaration of myFunction
int myOtherFunction(int m);  // Forward declaration of myOtherFunction

// Definition of myFunction
int myFunction(int n) {
    if (n < 0) {
        return 0;
    } else {
        return myOtherFunction(n - 2);
    }
}

// Definition of myOtherFunction
int myOtherFunction(int m) { return myFunction(m / 2); }

ifndef দিয়ে ইনক্লুড গার্ড

একটি প্রজেক্টে একই ফাইল একাধিকবার ইনক্লুড হলেও সমস্যা নেই। হেডার ফাইলে ডেফিনিশন থাকা উচিত নয়। পুরো প্রজেক্টে একই ডেফিনিশন একবারের বেশি থাকতে পারে না। এটাকে বলা হয় "One definition rule"। কম্পাইলার এটি প্রয়োগ করবে।

ইনক্লুড গার্ড দিয়ে একই ডেফিনিশনের অনিচ্ছাকৃত একাধিক ইনক্লুড এড়ানো সহজ। কম্পাইলেশনের ধাপগুলোতে একটি বিশেষ প্রক্রিয়ায় এগুলো গঠিত হয়। লক্ষ্য হলো, কোনো একটি নির্দিষ্ট ভ্যারিয়েবল সেট না থাকলেই কেবল ফাইলটি ইনক্লুড করা, আর ফাইলটি ইনক্লুড হওয়ার পর সেই ভ্যারিয়েবল সেট করা। প্রায়ই এই ভ্যারিয়েবলের জন্য ফাইলের নামের একটি ভিন্ন রূপ বেছে নেওয়া হয়। আরেকটি পদ্ধতি হলো একটি UUID তৈরি করা, যাতে ভুলে একই নাম দুবার ব্যবহারের ঝুঁকি কমে। নিচে সিনট্যাক্সটি দেখা যায়, যেখানে MY_HEADER_FILE_H একটি ভ্যারিয়েবল।

#ifndef MY_HEADER_FILE_H /* any name uniquely mapped to file name */
#define MY_HEADER_FILE_H

// file content

#endif

#pragma once ব্যবহারে সমস্যা হলো, প্রাগমা C++ ভাষার কোনো সরকারি অংশ নয়, আর এর ইমপ্লিমেন্টেশন কম্পাইলারভেদে আলাদা হয়। অনেক বড় প্রজেক্ট সহজ প্রাগমা পদ্ধতিতে চলে গেছে, তবে অল্প কিছু এখনো সতর্ক।

GitHub-এর মাধ্যমে সম্পাদনা করুন লিংকটি নতুন একটি উইন্ডো বা ট্যাবে খুলবে

হেডার শিখুন