ভেরিয়েবল এবং ডেটা টাইপ (Variables & Data Types)
সি++-এর সাথে পরিচিত হওয়া (Meet C++) — সি (C)-এর একটি উন্নত বা ইভলভড (Evolved) সংস্করণ
সি (C) যদি কোনো অমসৃণ রাস্তার (rugged) স্থপতি বা পাইওনিয়ার (pioneer) হয়ে থাকে, তবে সি++ (C++) হলো এমন একজন যে এর ওপর পিচ ঢেলে একে মজবুত (paved) করেছে, এর দুপাশে বিভিন্ন লেন (lane markings) বসিয়েছে এবং নিরাপত্তার জন্য বিভিন্ন স্থানে গার্ডরেইল (guardrails) বসিয়ে দিয়েছে। মূলত, এরা উভয়েই একই ধরনের ডিএনএ (DNA) — যেমন বিভিন্ন পয়েন্টার (pointers), ম্যানুয়াল মেমোরি (manual memory) ও স্পিড (compiled speed) শেয়ার করে থাকে — তবে সি++ (C++) মূলত সি-এর (C) বিভিন্ন জটিলতাগুলো থেকে অনেক কিছু শিখেছে এবং এতে আরও উন্নত টাইপ (better types), নিরাপদ ডিফল্ট (safer defaults) এবং আধুনিক সুবিধার (modern conveniences) সংযোজন করেছে।
বিশেষ করে আপনি কীভাবে এর ভেরিয়েবলগুলোকে (variables) ডিক্লেয়ার (declare) করবেন, তা এর চেয়ে স্পষ্ট করে আর কোথাও বলা নেই। সি (C) মূলত আপনাকে সবকিছুই নিজের হাতে (spell everything out) করার সুযোগ দেয়। কিন্তু সি++ (C++) বলে: "আমি আপনার হয়ে নিজেই এর অনেক কিছু করে বা বুঝে (figure out) নিতে পারি।"
মৌলিক টাইপগুলো (The Fundamental Types)
সি++ (C++) মূলত সি (C)-এর সমস্ত নিউমেরিক টাইপগুলোকেই (numeric types) উত্তরাধিকার সূত্রে বা ইনহেরিট (inherits) করে নিয়ে আসে এবং এতে জীবনযাত্রার মান উন্নত করার (quality-of-life) মতো বেশ কিছু ছোটখাটো আপগ্রেড (upgrades) যুক্ত করে দেয়:
int— পূর্ণ সংখ্যা বা whole numbers:42,-7,0। সাধারণত (Typically) এরা 32 বিটের (bits) হয়ে থাকে।double— উচ্চ নির্ভুলতা (high precision) বা প্রিসিশনযুক্ত দশমিক সংখ্যা বা decimal numbers:3.14159। ফ্লোটিং-পয়েন্ট বা দশমিকের (floating-point) যেকোনো গাণিতিক হিসেব-নিকেশে এটিই হলো আপনার ডিফল্ট (default)।float— এটিdouble-এর তুলনায় কিছুটা কম নির্ভুল দশমিক (less precise decimals) প্রদান করে এবং এটিdouble-এর অর্ধেক মেমোরি (half the memory) ব্যবহার করে। যখন আপনার মেমোরি নিয়ে কাজ করার অত্যন্ত প্রয়োজন হয় (যেমন গ্রাফিক্সের বা graphics কাজে), তখন এটিকে ব্যবহার করার চেষ্টা করুন।char— একটি একক অক্ষর বা single character:'A','7','\n'। এর পর্দার আড়ালে বা Under the hood, এটি মূলত একটি অত্যন্ত ছোট ইন্টিজার (small integer)।bool— এটি মূলতtrue(সত্য) বাfalse(মিথ্যা) রিটার্ন করে। সি (C)-তে এর জন্য মূলত আপনাকে একটি#include <stdbool.h>ব্যবহার করতে হতো — কিন্তু সি++ (C++)-এ, bool হলো একটি নেটিভ বা সম্পূর্ণ নিজস্ব, ফার্স্ট-ক্লাস টাইপ (first-class type)। এর মানে হলো এর জন্য আলাদা করে কোনো প্রকার হেডারের (header) প্রয়োজন নেই!std::string— এটি সত্যিই একটি অসাধারণ স্ট্রিং টাইপ (string type)! এখানে আর কোনোchar[]অ্যারে বাstrlen()নিয়ে জাগলিং (juggling) করার বা বিভ্রান্ত হওয়ার প্রয়োজন নেই। এটি নিজে থেকেই বৃদ্ধি পায় (grows), সঙ্কুচিত হয় (shrinks) এবং এর নিজস্ব মেমোরিকেও (memory) এটি নিজেই পরিচালনা (manages) করতে পারে।
বেসিক ডিক্লেয়ারেশন (Basic Declarations) — সি++ স্টাইল (C++ Style)
the auto কিওয়ার্ড (Keyword) — কম্পাইলারটিকে (Compiler) তার নিজের কাজ করতে দিন
ধরুন আপনি কোনো একটি রেস্তোরাঁয় গেছেন এবং এর ওয়েটার (waiter) আগে থেকেই আপনার পছন্দের অর্ডারটি (order) জানে, কারণ আপনি সেখানে প্রতিদিন খেতে যান। auto হলো ঠিক তেমনি একটি জিনিস — আপনি এই টাইপের নামটিকে এড়িয়ে বা স্কিপ (skip) করে যেতে পারেন এবং এর এখানকার কম্পাইলারটি (compiler) মূলত যেকোনো অ্যাসাইনমেন্ট থেকেই এটিকে অনুমান (deduces) করে নিতে পারে।
auto-টি মূলত C++11-এ যুক্ত (introduced) করা হয়েছিল এবং বেশ দ্রুতই এটি সবার কাছে অন্যতম জনপ্রিয় (most-used features) একটি ফিচারে পরিণত হয়েছিল। বিশেষ করে বিভিন্ন জটিল বা অসুন্দর (ugly) টাইপের নামের (যেমন ইটারেটরের বা iterators) ক্ষেত্রে এটি অত্যন্ত কাজে আসে, তবে এটি যেকোনো সাধারণ বা সিম্পল (simple) টাইপগুলোর সাথেও বেশ দুর্দান্তভাবে কাজ করে।
অ্যাকশনে auto (auto in Action)
auto মানে কিন্তু কোনো ধরনের "আনটাইপড (untyped)" নয় — এখানকার কম্পাইলারটি মূলত এর কম্পাইল টাইমেই (compile time) এর সঠিক টাইপটিকে (exact type) খুঁজে বের করে (figures out) নেয়। আর এটি এখনও বেশ স্ট্রংলি টাইপড (strongly typed)! একবার বুঝে নেওয়ার বা deduced হয়ে যাওয়ার পর এখানকার টাইপটি মূলত লক (locked in) হয়ে যায়। auto x = 5; মূলত x-কে একটি int-এ পরিণত করে, এবং আপনি পরবর্তী সময়ে এতে চাইলেই কোনো স্ট্রিং অ্যাসাইন (assign) করতে পারবেন না। এই auto-টিকে আপনি চাইলে জাভাস্ক্রিপ্টের (JavaScript) var-এর মতো না ভেবে, বরং এর একটি শর্টহ্যান্ড (shorthand) হিসেবে চিন্তা করতে পারেন।const বনাম (vs) constexpr — "মোদামোদি (Don't Touch)"-এর দুটি ভিন্ন স্বাদ (Flavors)
সি (C)-তে মূলত const ছিল এবং সি++ (C++)-ও এটিকে আগের জায়গাতেই রেখে দিয়েছে — তবে এটি তার কম্পাইল-টাইম কনস্ট্যান্টগুলোর (compile-time constants) জন্য এতে একটি নতুন constexpr যুক্ত করেছে। আপনি চাইলে এটিকে ঠিক এভাবেও চিন্তা করতে পারেন:
const— এর মানে হলো "এই মানটি মূলত এর রানটাইমে (runtime) কখনোই পরিবর্তন (change) করা হবে না।" যখন কোনো একটি প্রোগ্রাম রান (runs) করে তখন এর মানটিকে হিসাব (computed) করা হতে পারে, তবে একবার সেট (set) হয়ে যাওয়ার পর, এটিকে লক (locked) করে দেওয়া হয়।constexpr— এর মানে হলো "এই মানটি কম্পাইল টাইমেই (compile time) জানা হয়ে গেছে।" কোনো একটি প্রোগ্রাম রান হওয়ার আগেই এর কম্পাইলারটি মূলত এর মানটিকে হিসেব বা compute করে ফেলে, যার মানে হলো যেসব জায়গায় কম্পাইল-টাইম কনস্ট্যান্টের (compile-time constants) প্রয়োজন পড়ে, ঠিক সেখানেই আপনি চাইলে এটিকে ব্যবহার করতে পারবেন (যেমন বিভিন্ন অ্যারে সাইজ বা array sizes)।
const বনাম (vs) constexpr
{}-এর সাহায্যে ইউনিফর্ম ইনিশিয়ালাইজেশন (Uniform Initialization)
সি++ (C++) মূলত কোনো কিছুকে ইনিশিয়ালাইজ (initialize) করার জন্য অসংখ্য ভিন্ন ভিন্ন উপায়ের (too many ways) এক বিরক্তিকর ইতিহাস বহন করে আসছে। এখানকার এই বিশৃঙ্খলা (chaos) থেকে রেহাই পেতে C++11 মূলত এই কার্লি ব্রেসেসগুলো বা curly braces {}-কে ব্যবহারের মাধ্যমে একটি ইউনিফর্ম বা সমান ইনিশিয়ালাইজেশন (uniform initialization) পদ্ধতি চালু করেছিল। এর মূল সুবিধাটি (benefit) কী? এটি মূলত যেকোনো ধরনের ন্যারোয়িং কনভার্সনগুলোকে (narrowing conversions) বা ছোটখাটো রূপান্তরগুলোকে প্রতিরোধ (prevents) করে থাকে — অর্থাৎ এটি মূলত বিভিন্ন সাইলেন্ট ডেটা লসকে (silent data loss) প্রতিরোধ করে, যা সি (C) প্রোগ্রামিংয়ে অহরহ ঘটতে দেখা যায়。
এখানকার এই {}-টিকে আপনি চাইলে একজন অত্যন্ত কঠোর অভিভাবকের (strict parent) মতো চিন্তা করতে পারেন: এটি আপনাকে কখনোই চুপি চুপি বা silently একটি double-কে কোনো int-এর ভেতরে ঠেলে দিয়ে আপনার মূল্যবান দশমিকের (decimals) মানগুলোকে হারাতে (lose) দেবে না。
{}-এর সাহায্যে ইউনিফর্ম ইনিশিয়ালাইজেশন (Uniform Initialization)
C++ string বনাম C's char[]
সি (C)-তে, স্ট্রিং (strings) বলতে মূলত এক ধরনের ক্যারেক্টারের অ্যারেকে (arrays of characters) বোঝায়, যা একটি নাল বাইট (null byte) '\0' দিয়ে শেষ হয়। এক্ষেত্রে আপনাকে নিজের হাতেই এর দৈর্ঘ্য বা লেন্থগুলো (lengths) পরিচালনা করতে হতো, বাফার বরাদ্দ (allocate buffers) করতে হতো এবং আপনাকে সব সময় দোয়া করতে হতো যেন আপনি এগুলোকে অতিক্রম বা overrun করে না ফেলেন। সি++ (C++)-এর std::string মূলত আপনার হয়ে এই সকল কাজ নিজেই করে নেয় (handles) — এটি স্বয়ংক্রিয়ভাবেই বৃদ্ধি পায় (grows), এটি এর নিজস্ব দৈর্ঘ্য (length) সম্পর্কেও অবগত থাকে এবং এটি যেকোনো কিছুকে strcmp()-এর পরিবর্তে সরাসরি ==-এর সাহায্যে তুলনা করাকে (comparison) সমর্থন করে থাকে。