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

অপারেটর এবং এক্সপ্রেশন (Operators & Expressions)

সি-এর (C) সকল অপারেটরের পাশাপাশি এখানকার ভেতরে আরও কিছু নতুন কৌশল (new tricks) লুকিয়ে আছে

সি-এর আপগ্রেড করা টুলবক্স (C's Toolbox, Upgraded)

যদি সি (C) আপনাকে হাতুড়ি, স্ক্রুড্রাইভার এবং রেঞ্চের মতো বিভিন্ন শক্তিশালী টুলবক্স (toolbox) দিয়ে থাকে, তবে সি++ (C++) মূলত ওই একই টুলবক্সের ভেতর একটি পাওয়ার ড্রিল, একটি লেজার লেভেল এবং একটি লেবেল মেকারকে যুক্ত (added) করে দিয়েছে। সি (C) প্রোগ্রামিং থেকে আপনি যত অপারেটরের কাজ জানেন, সি++ (C++)-এ তার সবকিছুই ঠিক একইভাবেই (exactly the same) কাজ করে। তবে সি++ (C++) এটিকে আরও বেশি এক্সপ্রেসিভ বা পরিষ্কার (expressive) করে তুলতে এতে আরও নতুন (new) কিছু অপারেটর যুক্ত করেছে।

চলুন পরিচিত কিছু অপারেটরগুলোর (familiar operators) মাধ্যমেই শুরু করা যাক, এবং এরপর আমরা এর নতুন অপারেটরগুলো (newcomers) সম্পর্কে জানব।

পরিচিত অপারেটরগুলো (The Familiar Operators)

সি-এর (C) সকল অপারেটরগুলোই এখানে অপরিবর্তিত (unchanged) অবস্থায় রয়েছে:

  • অ্যারিথমেটিক বা গাণিতিক (Arithmetic): +, -, *, /, % (ভাগশেষ বা modulo)
  • কম্পারিজন বা তুলনামূলক (Comparison): ==, !=, <, >, <=, >=
  • লজিক্যাল বা যৌক্তিক (Logical): && (অ্যান্ড বা AND), || (অর বা OR), ! (নট বা NOT)
  • বিটওয়াইজ (Bitwise): &, |, ^, ~, <<, >>
  • অ্যাসাইনমেন্ট (Assignment): =, +=, -=, *=, /=, %=, &=, |=, ^=
  • ইনক্রিমেন্ট/ডিক্রিমেন্ট (Increment/Decrement): ++, -- (প্রিফিক্স বা prefix এবং পোস্টফিক্স বা postfix)
  • টার্নারি (Ternary): condition ? valueIfTrue : valueIfFalse

এগুলো মূলত সি-এর (C) মতোই একইভাবে (identically) কাজ করে। তাই এখানে অবাক (surprises) হওয়ার মতো কিছুই নেই।

সি++ এর পরিচিত অপারেটরগুলো (Familiar Operators in C++)

#include <iostream>
using namespace std;
int main() {
int a = 17, b = 5;
cout << "a + b = " << (a + b) << endl; // 22
cout << "a / b = " << (a / b) << endl; // ৩ (ইন্টিজার ডিভিশন বা integer division!)
cout << "a % b = " << (a % b) << endl; // ২ (ভাগশেষ বা remainder)
// সি-এর (C) মতো শুধু 0/1 নয়, এখানকার কম্পারিজন বা Comparison মূলত একটি বুলিয়ান বা bool মান (true/false) রিটার্ন করে
cout << boolalpha;
cout << "a > b = " << (a > b) << endl; // true
cout << "a == b = " << (a == b) << endl; // false
// টার্নারি অপারেটর বা Ternary
string result = (a > b) ? "a wins" : "b wins";
cout << result << endl;
return 0;
}
Output
a + b  = 22
a / b  = 3
a % b  = 2
a > b  = true
a == b = false
a wins

নতুন সদস্য: স্কোপ রেজোলিউশন (Scope Resolution বা ::)

এই :: অপারেটরটি হলো মূলত সি++ (C++)-এর "আমি ঠিক এটিকে (this specific) বোঝাতে চেয়েছি" বলার একটি দারুণ উপায়। এটি মূলত বিভিন্ন নামের মধ্যে থাকা অ্যাম্বিগুইটি বা অস্পষ্টতা (ambiguity) দূর করতে সাহায্য করে — অনেকটা কোনো একটি ঘরে "অ্যালেক্স" নামের দুজন ব্যক্তি থাকলে, তাদের মধ্যে "অ্যাকাউন্টিংয়ের অ্যালেক্স" এবং "ইঞ্জিনিয়ারিংয়ের অ্যালেক্স"-কে আলাদা করে চিহ্নিত করার মতো।

আপনি মূলত এই ::-টিকে সব জায়গাতেই দেখতে পাবেন: যেমন বিভিন্ন নেমস্পেস মেম্বারে (namespace members) (যেমন std::cout) অ্যাক্সেস (accessing) করার ক্ষেত্রে, ক্লাসের বাইরে তার বিভিন্ন মেথড (class methods) ডিফাইন বা সংজ্ঞায়িত করার ক্ষেত্রে, এবং বিভিন্ন লোকাল ভ্যারিয়েবলের (local variables) আড়ালে লুকিয়ে থাকা গ্লোবাল ভ্যারিয়েবলগুলোতে (global variables) পৌঁছানোর (reaching) ক্ষেত্রে।

স্কোপ রেজোলিউশন অপারেটর বা Scope Resolution Operator (::)

#include <iostream>
using namespace std;
int value = 100; // গ্লোবাল বা Global ভ্যারিয়েবল
namespace Math {
double pi = 3.14159;
}
int main() {
int value = 42; // এখানকার এই লোকাল বা local ভ্যারিয়েবলটি মূলত গ্লোবাল (global) ভ্যারিয়েবলটিকে ঢেকে দেয় (shadows)
cout << "Local value: " << value << endl; // 42
cout << "Global value: " << ::value << endl; // ১০০ — এই ::-টি মূলত গ্লোবালটিতে (global) পৌঁছায় বা reaches
cout << "Math::pi: " << Math::pi << endl; // 3.14159
// std::cout-ও মূলত এই std নেমস্পেস (namespace) থেকে cout-কে অ্যাক্সেস (access) করতে :: ব্যবহার করে
// (কিন্তু ওপরের 'using namespace std;'-এর কারণে আমরা এটিকে এড়িয়ে বা skip যাই)
return 0;
}
Output
Local value:  42
Global value: 100
Math::pi:     3.14159

স্ট্রিম অপারেটর: << এবং >> (Stream Operators: << and >>)

সি++ (C++)-এর অন্যতম একটি সেরা কাজ মূলত এখানেই রয়েছে। সি (C) প্রোগ্রামিংয়ে এই << এবং >> মূলত বিটওয়াইজ শিফট অপারেটরগুলো (bitwise shift operators) হিসেবে কাজ করে — এগুলো মূলত বিভিন্ন বিটকে (bits) ডানে (right) বা বামে (left) সরিয়ে (shift) দেয়। সি++ (C++) তার ইন্টিজারের (integers) জন্য এর এই ধর্মটিকে বা ব্যবহারটিকে বজায় রাখলেও, বিভিন্ন আই/ও (I/O) স্ট্রিমগুলোর জন্য সে এটিকে ওভারলোড (overloads) করেছে।

এক্ষেত্রে আপনি <<-টিকে "স্ট্রিমে ডেটা পুশ (push) করা" হিসেবে (যেমন স্ক্রিনের দিকে যাওয়া কোনো কনভেয়র বেল্টের ওপরে আইটেমগুলো রাখা) এবং >>-টিকে "স্ট্রিম থেকে ডেটা তুলে (pull) আনা" হিসেবে (যেমন কিবোর্ড থেকে আসা কোনো বেল্ট থেকে আইটেমগুলো তুলে নেয়া) কল্পনা করতে পারেন।

cout << চেইনিং (Chaining) — বেল্টের ওপরে আইটেমগুলো (Items) রাখা

#include <iostream>
using namespace std;
int main() {
string name = "Charlie";
int age = 30;
double gpa = 3.85;
// << চেইনিং (chaining) — এখানকার প্রতিটি << মূলত আউটপুট স্ট্রিমের (output stream) ওপরে আরও একটি করে আইটেম পুশ (pushes) করে
cout << "Name: " << name << ", Age: " << age << ", GPA: " << gpa << endl;
// তবে ইন্টিজারগুলোর বা integers-এর জন্য এই << এখনও একটি বিটওয়াইজ শিফট (bitwise shift):
int x = 1;
cout << "1 << 3 = " << (1 << 3) << endl; // বিটওয়াইজ বা bitwise: 1-কে বাম দিকে 3 ঘর শিফট (shifted left) করলে = 8 হয়
cout << "16 >> 2 = " << (16 >> 2) << endl; // বিটওয়াইজ বা bitwise: 16-কে ডান দিকে 2 ঘর শিফট (shifted right) করলে = 4 হয়
return 0;
}
Output
Name: Charlie, Age: 30, GPA: 3.85
1 << 3 = 8
16 >> 2 = 4
Note: সি (C) প্রোগ্রামিংয়ে << এবং >> হলো বিটওয়াইজ শিফট (bitwise shift) অপারেটর, কিন্তু সি++ (C++)-এ এগুলোকে স্ট্রিমের জন্য মূলত আই/ও-তে ওভারলোড (overloaded for I/O) করা হয়েছে। এক্ষেত্রে কন্টেক্সটের (Context) ভূমিকা বেশ গুরুত্বপূর্ণ! এগুলোকে মূলত যখন cout/cin-এর সাহায্যে ব্যবহার করা হয়, তখন এগুলো স্ট্রিম অপারেটর (stream operators) হিসেবে কাজ করে। এবং যখন এগুলোকে বিভিন্ন ইন্টিজারের (integers) সাথে ব্যবহার করা হয়, তখন এগুলো বিটওয়াইজ শিফট (bitwise shift) হিসেবে কাজ করে। এখানকার অবজেক্টের বা অপারেন্ডের টাইপের (operand types) ওপর ভিত্তি করে মূলত এর কম্পাইলারটি (compiler) ঠিক করে যে কোনটির ক্ষেত্রে সে কোনটিকে ব্যবহার করবে।

new এবং delete — সি++ স্টাইলের (C++ Style) ম্যানুয়াল মেমোরি

সি (C) প্রোগ্রামিং মূলত malloc() এবং free() নিয়ে কাজ করে। কিন্তু সি++ (C++) মূলত new এবং delete নিয়ে এসেছে — যেগুলো কোনো সাধারণ মেথডস বা ফাংশন নয়, এগুলো হলো এক ধরনের অপারেটর (operators), যা মেমোরি অ্যালোকেট (allocate) বা বরাদ্দ করতে পারে এবং এর কনস্ট্রাক্টর/ডেস্ট্রাক্টরগুলোকে কল (call) করতে পারে। এগুলো মূলত এর টাইপের দিক থেকে অত্যন্ত নিরাপদ (type-safe) এবং অনেক বেশি ক্লিন বা পরিষ্কার (cleaner)। তবে একটি আধুনিক সি++ (C++) মূলত new/delete দিয়ে বিভিন্ন র বা raw মেমোরি পরিচালনার চেয়ে স্মার্ট পয়েন্টারগুলোর (smart pointers) ব্যবহারটিকেই বেশি প্রাধান্য বা পছন্দ (prefers) করে।

new এবং delete

#include <iostream>
using namespace std;
int main() {
// হিপের (heap) ওপর একটি একক ইন্টিজার বা single int মেমোরি অ্যালোকেট (Allocate) করা বা বরাদ্দ করা
int* p = new int{42};
cout << "*p = " << *p << endl;
delete p; // মেমোরিটিতে (memory) ফাঁকা বা free করে দেওয়া
// হিপের (heap) ওপর একটি অ্যারে (array) অ্যালোকেট (Allocate) বা বরাদ্দ করা
int* arr = new int[5]{10, 20, 30, 40, 50};
for (int i = 0; i < 5; i++) {
cout << arr[i] << " ";
}
cout << endl;
delete[] arr; // অ্যারেগুলোর (arrays) জন্য delete[] ব্যবহার (use) করুন!
return 0;
}
Output
*p = 42
10 20 30 40 50

sizeof এবং typeid — বিভিন্ন টাইপ (Types) খুঁজে বের করা

এই sizeof মূলত একদম সি-এর (C) মতোই কাজ করে — এটি আপনাকে বাইট (bytes) হিসেবে কোনো কিছুর আকার বা সাইজটি (size) বলে দেয়। কিন্তু সি++ (C++)-এ এর সাথে আরও typeid (এটি <typeinfo> থেকে আসে) যুক্ত করা হয়েছে, যা মূলত আপনাকে রানটাইমে (runtime) যেকোনো একটি ভ্যারিয়েবলের (variable) আসল টাইপটিকে (actual type) পরিদর্শন করতে বা দেখতে (inspect) সাহায্য করে। এটি মূলত auto-এর ক্ষেত্রে বেশ দুর্দান্তভাবে (especially useful) কাজ করে — বিশেষত যখন আপনি নিশ্চিত হতে পারেন না যে কম্পাইলারটি আসলে ঠিক কোন টাইপটি (type) অনুমান (deduced) করেছে।

sizeof এবং typeid

#include <iostream>
#include <typeinfo>
#include <string>
using namespace std;
int main() {
auto x = 42;
auto y = 3.14;
auto z = 'A';
auto w = true;
auto s = string("hello");
cout << "sizeof(int): " << sizeof(int) << " bytes" << endl;
cout << "sizeof(double): " << sizeof(double) << " bytes" << endl;
cout << "sizeof(char): " << sizeof(char) << " byte" << endl;
cout << "sizeof(bool): " << sizeof(bool) << " byte" << endl;
cout << endl;
// এই typeid-টি মূলত আমাদের প্রকাশ করে বা reveals দেখায় যে auto আসলে ঠিক কী অনুমান (deduced) করেছে:
cout << "x is: " << typeid(x).name() << endl;
cout << "y is: " << typeid(y).name() << endl;
cout << "z is: " << typeid(z).name() << endl;
cout << "w is: " << typeid(w).name() << endl;
return 0;
}
Output
sizeof(int):    4 bytes
sizeof(double): 8 bytes
sizeof(char):   1 byte
sizeof(bool):   1 byte

x is: i
y is: d
z is: c
w is: b

দ্রষ্টব্য (Note): typeid().name()-এর আউটপুটটি (output) মূলত এর কম্পাইলারের ওপর নির্ভরশীল (compiler-dependent)। যদি আপনি জিসিসি (GCC) ব্যবহার করেন তবে এটি আপনাকে i (ইন্টিজার বা int), d (ডাবল বা double), c (ক্যারেক্টার বা char), b (বুলিয়ান বা bool)-এর মতো বিভিন্ন ছোট ছোট কোডগুলো (short codes) দেখাবে। আর যদি আপনি এমএসভিসি (MSVC) ব্যবহার করেন তবে এটি আপনাকে আরও সহজে পড়া যায় এমন বা পঠনযোগ্য (readable) নাম দেখাবে। তবে এর দুইটির আউটপুট নাম (names) আলাদা হলেও, তারা মূলত আপনাকে এটিই জানিয়ে দেয় যে এখানকার কম্পাইলারটি (compiler) আসলে ওই অবজেক্টের টাইপটিকে (type) ঠিক কী বলে চিন্তা (thinks) বা বিবেচনা করছে।

চ্যালেঞ্জ

ছোট কুইজ

সি++ (C++)-এ এই :: অপারেটরটি মূলত কী কাজ করে?
Variables & Data TypesInput & Output