Lesson পড়তে ৮ মিনিট লাগবে

ভেরিয়েবল এবং ডেটা টাইপ (Variables & Data Types)

সি++ (C++) হলো সি (C) প্রোগ্রামিংয়ের সেই ছোট ভাই, যে মূলত তার বড় ভাই সি-এর (C) বিভিন্ন ভুল থেকে অনেক কিছু শিখেছে — এদের ডিএনএ (DNA) বা ধরন মূলত একই রকম হলেও এর আদবকায়দাগুলো (manners) বেশ চমৎকার

সি++-এর সাথে পরিচিত হওয়া (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)

#include <iostream>
#include <string>
using namespace std;
int main() {
int age = 25;
double pi = 3.14159;
float gravity = 9.81f; // ফ্লোট লিটারালগুলোর (float literals) শেষাংশে বা সাফিক্সে (suffix) একটি f যুক্ত হবে
char grade = 'A';
bool isPassing = true; // এটি মূলত একটি নেটিভ বুল (native bool) — এর জন্য আলাদা করে কোনো হেডারের (header) প্রয়োজন নেই!
string name = "Alice"; // এটি মূলত একটি রিয়েল স্ট্রিং টাইপ (real string type), এটি কোনো char[] নয়
cout << name << " is " << age << " years old" << endl; // এর বয়স বা is ... বছর বা years old
cout << "Pi: " << pi << endl; // পাই বা Pi:
cout << "Passing: " << boolalpha << isPassing << endl; // পাসিং বা Passing:
return 0;
}
Output
Alice is 25 years old
Pi: 3.14159
Passing: true

the auto কিওয়ার্ড (Keyword) — কম্পাইলারটিকে (Compiler) তার নিজের কাজ করতে দিন

ধরুন আপনি কোনো একটি রেস্তোরাঁয় গেছেন এবং এর ওয়েটার (waiter) আগে থেকেই আপনার পছন্দের অর্ডারটি (order) জানে, কারণ আপনি সেখানে প্রতিদিন খেতে যান। auto হলো ঠিক তেমনি একটি জিনিস — আপনি এই টাইপের নামটিকে এড়িয়ে বা স্কিপ (skip) করে যেতে পারেন এবং এর এখানকার কম্পাইলারটি (compiler) মূলত যেকোনো অ্যাসাইনমেন্ট থেকেই এটিকে অনুমান (deduces) করে নিতে পারে।

auto-টি মূলত C++11-এ যুক্ত (introduced) করা হয়েছিল এবং বেশ দ্রুতই এটি সবার কাছে অন্যতম জনপ্রিয় (most-used features) একটি ফিচারে পরিণত হয়েছিল। বিশেষ করে বিভিন্ন জটিল বা অসুন্দর (ugly) টাইপের নামের (যেমন ইটারেটরের বা iterators) ক্ষেত্রে এটি অত্যন্ত কাজে আসে, তবে এটি যেকোনো সাধারণ বা সিম্পল (simple) টাইপগুলোর সাথেও বেশ দুর্দান্তভাবে কাজ করে।

অ্যাকশনে auto (auto in Action)

#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
auto count = 10; // int
auto price = 9.99; // double
auto letter = 'Z'; // char
auto isValid = false; // bool
auto greeting = string("Hello"); // std::string
// যেকোনো জটিল টাইপের (complex types) ক্ষেত্রে auto মূলত দুর্দান্তভাবে কাজ করে বা shines:
vector<int> nums = {1, 2, 3, 4, 5};
auto it = nums.begin(); // vector<int>::iterator — এটি আপনার প্রায় ২৫টি (25) ক্যারেক্টার বা characters বাঁচিয়ে (saved) দিয়েছে!
cout << "count is " << count << " (type deduced as int)" << endl; // (টাইপটি মূলত একটি ইন্টিজার বা int হিসেবে ধরে নেওয়া বা deduced হয়েছে)
cout << "price is " << price << " (type deduced as double)" << endl; // (টাইপটি মূলত একটি ডাবল বা double হিসেবে ধরে নেওয়া বা deduced হয়েছে)
cout << "*it is " << *it << endl;
return 0;
}
Output
count is 10 (type deduced as int)
price is 9.99 (type deduced as double)
*it is 1
Note: 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

#include <iostream>
using namespace std;
int main() {
const int maxRetries = 3; // রানটাইম কনস্ট্যান্ট (runtime constant) — এর মান শুধু একবারই সেট (set) করা হয়
constexpr double pi = 3.14159265; // কম্পাইল-টাইম কনস্ট্যান্ট (compile-time constant) — যা মূলত বাইনারিতে বা binary-তে রূপান্তরিত বা baked হয়ে যায়
constexpr int arraySize = 10;
int scores[arraySize]; // ✅ constexpr মূলত বিভিন্ন অ্যারে সাইজ (array size) হিসেবে কাজ করে
// int other[maxRetries]; // ✅ এটি এখানেও কাজ করে কারণ এখানকার মানটি সবারই জানা বা known
// ফাংশনের (functions) সাহায্যে মূলত এখানকার আসল পার্থক্যটিকে (difference) তুলে ধরা হয়েছে:
// একটি constexpr-এর মানগুলো (values) অবশ্যই এর কম্পাইল টাইমেই (compile time) গণনাযোগ্য বা computable হতে হবে
// const-এর মানগুলোকে (values) মূলত কোনোভাবেই পুনরায় অ্যাসাইন বা reassigned করা যায় না
const int userInput = 42; // ✅ const — রানটাইমে (runtime) অ্যাসাইন (assigned) করা মানগুলোর (value) ক্ষেত্রে এটি বেশ দুর্দান্ত বা fine
// constexpr int x = userInput; // ❌ এরর (error) — এখানকার userInput-টি কোনোভাবেই একটি কম্পাইল-টাইম কনস্ট্যান্ট (compile-time constant) নয়
cout << "Max retries: " << maxRetries << endl; // ম্যাক্স রিট্রাইস (Max retries):
cout << "Pi: " << pi << endl;
cout << "Array size: " << arraySize << endl; // অ্যারের সাইজ বা Array size:
return 0;
}
Output
Max retries: 3
Pi: 3.14159265
Array size: 10

{}-এর সাহায্যে ইউনিফর্ম ইনিশিয়ালাইজেশন (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)

#include <iostream>
#include <string>
using namespace std;
int main() {
// ইনিশিয়ালাইজেশনের (initialization) প্রচলিত সি-স্টাইল (C-style)
int a = 42;
double b = 3.14;
// ইউনিফর্ম ইনিশিয়ালাইজেশন (Uniform initialization) (C++11)
int c{42};
double d{3.14};
string name{"Bob"};
bool flag{true};
// এখানকার সবচেয়ে বড় জয় (The big win) — এটি মূলত যেকোনো ন্যারোয়িংকে প্রতিরোধ করে (prevents narrowing):
// int narrow{3.14}; // ❌ কম্পাইলার এরর বা compiler ERROR — এটি মূলত .14 কে হারিয়ে বা lose করে ফেলবে
int oldWay = 3.14; // ⚠️ খুব সহজেই কম্পাইল (compiles) হয়ে যাবে, এটি মূলত নীরবেই বা silently যেকোনো কিছুকে 3-এ ট্রাঙ্কেট (truncates) করে বা রূপান্তর করে ফেলে!
cout << "c = " << c << endl;
cout << "d = " << d << endl;
cout << "name = " << name << endl;
cout << "oldWay = " << oldWay << " (silently lost .14!)" << endl; // (এক্ষেত্রে .14 নীরবেই হারিয়ে গেছে বা silently lost!)
return 0;
}
Output
c = 42
d = 3.14
name = Bob
oldWay = 3 (silently lost .14!)

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) সমর্থন করে থাকে。

C++ string — একটি রিয়েল স্ট্রিং টাইপ (A Real String Type)

#include <iostream>
#include <string>
using namespace std;
int main() {
string first = "Hello";
string second = "World";
// কনক্যাটেনেশন (Concatenation) — শুধু একটি + ব্যবহার করুন
string greeting = first + ", " + second + "!";
cout << greeting << endl;
// লেন্থ বা Length — এখানে কোনো strlen()-এর প্রয়োজন নেই
cout << "Length: " << greeting.length() << endl;
// তুলনা (Comparison) — শুধু একটি == ব্যবহার করুন
if (first == "Hello") {
cout << "Strings match!" << endl; // স্ট্রিংগুলো মিলেছে বা Strings match!
}
// সাবস্ট্রিং বা Substring, ফাইন্ড বা find, এবং আরও অনেক বিল্ট-ইন (built-in) সুবিধা রয়েছে
cout << "Substring: " << greeting.substr(0, 5) << endl;
cout << "Found 'World' at index: " << greeting.find("World") << endl; // 'World' পাওয়া গেছে ইনডেক্স বা index:
return 0;
}
Output
Hello, World!
Length: 13
Strings match!
Substring: Hello
Found 'World' at index: 7
চ্যালেঞ্জ

ছোট কুইজ

auto x = 3.14; মূলত x-এর টাইপ (type) হিসেবে ঠিক কোনটিকে ডিডিউস (deduce) করে বা বেছে নেয়?
Why Learn C++?Operators & Expressions