মডিউল ৩-০ঃ সূচনা
এই মডিউলে আমরা কি কি শিখবোঃ
- ক্লাস এবং অবজেক্ট কখন দরকার তা জানবো
- ক্লাস এবং অবজেক্ট কিভাবে তৈরি করতে হয় তা দেখব
- ডায়নামিক অবজেক্ট তৈরি করা দেখব
- কন্সট্রাক্টর এবং এরে সাইন এর কাজ দেখব
- সর্ট ফাংশনের মাধ্যমে এরে সর্ট করা দেখব
মডিউল ৩-১ঃ কেন আমাদের ক্লাস এবং অবজেক্ট প্রয়োজন
মনে করি আমাদেরকে অনেকগুলো স্টুডেন্ট এর ডাটা স্টোর রাখতে হবে। প্রতিটি স্টুডেন্ট এর নাম, রোল, সিজিপিএ এই টাইপ ইনফোরমেশন স্টোর করতে হবে। তাহলে এটি আমরা নরমালি করতে গেলে অনেকগুলো ভেরিয়েবল, অনেকগুলো এরের সাহায্যে করতে হবে। তারপরও ওরগানাইজভাবে হবে না। কোন সিজিপিএ কোন স্টুডেন্ট এর তা বের করতে যথেষ্ট বেগ পেতে হবে।
এই টাইপ সিচুয়েশনে আমাদের নিজেদের মতো ডাটা টাইপ প্রয়োজন। সি++ লেঙ্গুয়েজে এরকম ডাটা টাইপ আছে। যার নাম ক্লাস। এটিকে ইউজার ডিফাইনড ডাটা টাইপও বলা হয়।
উপরের সিচুয়েশনটা আমরা যেভাবে সল্ভ করতে পারি তা হলো এইযে প্রতিটি স্টুডেন্ট এর নাম, রোল, সিজিপিএ এগুলো আমরা একটি গ্রুপ করে রেখে দিতে পারি। তারপর প্রতিটি স্টুডেন্ট এর জন্য এরকম আলাদা আলাদা গ্রুপ খুলতে পারি। তাহলে আমাদের যখনি কোন স্টুডেন্ট এর ইনফোরমেশন প্রয়োজন হবে আমরা সেই স্টুডেন্ট এর গ্রুপে যেয়ে তার নাম, রোল, সিজিপিএ সব পেয়ে যাব। এখানে গ্রুপটা হচ্ছে আমাদের ক্লাস এবং একেকজন স্টুডেন্ট হচ্ছে একেকটি অবজেক্ট।
মডিউল ৩-২, ৩-৩, ৩-৪ঃ কিভাবে ক্লাস এবং অবজেক্ট ক্রিয়েট করতে হয়
সি++ এ ক্লাস ক্রিয়েট করার সিন্টেক্সঃ
class class_name [ নরমালি ক্যাপিটাল লেটারে লিখা হয়। না লিখলেও কাজ করবে ]
{
public: [ access_modifier টোটাল ৩ ধরনের হয়ঃ Public, Private, Protected ]
data_type variable_name;
data_type variable_name;
.........
};সি++ এ অবজেক্ট ক্রিয়েট করার সিন্টেক্সঃ
class_name object_name;
object_name.class_variable [ অবজেক্ট এর ক্লাস ভেরিয়েবলগুলো এক্সেস করতে হলে . (ডট) ব্যাবহার করতে হয় ]সম্পূর্ণ কোডঃ
#include <bits/stdc++.h>
using namespace std;
class Student // ক্লাস ক্রিয়েট করা হয়েছে।
{
public: // এক্সেস মডিফায়ার হিসেবে পাবলিক দেওয়া হয়েছে।
char name[100]; // ক্লাস ভেরিয়েবল ডিক্লেয়ার করা হয়েছে।
int roll;
double cgpa;
};
int main()
{
Student a, b; // Student ক্লাসের অবজেক্ট ক্রিয়েট করা হচ্ছে ২টি।
cin.getline(a.name, 100); // শুরুতে প্রথম অবজেক্টের নাম ইনপুট নেয়া হচ্ছে। নামের মধ্যে স্পেস থাকতে পারে তাই গেটলাইন দিয়ে ইনপুট নেওয়া হয়েছে।
cin >> a.roll >> a.cgpa; // তারপর প্রথম অবজেক্টের রোল এবং সিজিপিএ ইনপুট নেওয়া হচ্ছে।
getchar(); // প্রথম অবজেক্টের রোল এবং সিজিপিএ ইনপুট দেওয়ার পর একটি এন্টার দেওয়া হবে তারপর দ্বিতীয় অবজেক্টের নাম ইনপুট দেওয়া হবে। সেই এন্টারটি ইগনোর করার জন্য getchar() ইউজ করা হয়েছে।
cin.getline(b.name, 100); // দ্বিতীয় অবজেক্টের নাম ইনপুট নেয়া হচ্ছে।
cin >> b.roll >> b.cgpa; // তারপর দ্বিতীয় অবজেক্টের রোল এবং সিজিপিএ ইনপুট নেওয়া হচ্ছে।
cout << a.name << " " << a.roll << " " << a.cgpa << endl; // প্রথম অবজেক্ট এর নাম, রোল, সিজিপিএ প্রিন্ট করা হচ্ছে।
cout << b.name << " " << b.roll << " " << b.cgpa << endl; // দ্বিতীয় অবজেক্ট এর নাম, রোল, সিজিপিএ প্রিন্ট করা হচ্ছে।
return 0;
}এভাবে আমরা প্রয়োজন মতো ক্লাস এবং অবজেক্ট ক্রিয়েট করে নিতে পারি।
মডিউল ৩-৫ঃ কন্সট্রাক্টর, দিস পয়েন্টার এবং এরো সাইন
#include <bits/stdc++.h>
using namespace std;
class Student
{
public:
int roll;
int cls;
double gpa;
};
int main()
{
Student rahim; // অবজেক্ট ডিক্লেয়ার করা হয়েছে।
rahim.roll = 29; // অবজেক্টের ভেলু এসাইন করে দেওয়া হয়েছে।
rahim.cls = 9;
rahim.gpa = 5.00;
Student karim; // আরেকটি অবজেক্ট ডিক্লেয়ার করা হয়েছে।
karim.roll = 45; // অবজেক্টের ভেলু এসাইন করে দেওয়া হয়েছে।
karim.cls = 10;
karim.gpa = 4.33;
cout << rahim.roll << " " << rahim.cls << " " << rahim.gpa << endl; // প্রথম অবজেক্ট প্রিন্ট করা হয়েছে।
cout << karim.roll << " " << karim.cls << " " << karim.gpa << endl; // দ্বিতীয় অবজেক্ট প্রিন্ট করা হয়েছে।
return 0;
}উপরের কোডে আমরা দেখতে পাচ্ছি, আমাদের প্রতিবার একটি করে অবজেক্ট ডিক্লেয়ার করার পর তার ভেলু এসাইন করে দিতে হচ্ছে। এই কাজটি প্রতিবার না করে অবজেক্ট ডিক্লেয়ার করার সময় এক লাইনেই আমরা অবজেক্ট এর ভেলু এসাইন করে দিতে পারি। এটির জন্য আমাদের ব্যাবহার করতে হবে কন্সট্রাক্টর।
কন্সট্রাক্টর হচ্ছে এমন একটি স্পেশাল ফাংশন যা ক্লাসের ভেতরে থাকে, যার নাম এবং ক্লাসের নাম সেইম থাকে এবং কন্সট্রাক্টর এর কোন রিটার্ন টাইপ থাকে না। কোন অবজেক্ট যখন ডিক্লেয়ার করা হয় তখন কন্সট্রাক্টর অটোমেটিক কল হয়। অবজেক্ট ডিক্লেয়ার করার সময় কন্সট্রাক্টরকে কল করে তার ভেলুগুলো দিয়ে দিলে কন্সট্রাক্টর ভেলুগুলো অবজেক্টের ভেরিয়েবল এর মধ্যে এসাইন করে দেয়।
#include <bits/stdc++.h>
using namespace std;
class Student
{
public:
int roll;
int cls;
double gpa;
Student(int r, int c, double g) // কন্সট্রাক্টর যা রোল, ক্লাস এবং জিপিএ প্যারামিটার হিসেবে নিচ্ছে। এখানে প্যারামিটারের মধ্যে ভেরিয়েবল এর নাম ক্লাস ভেরিয়েবল এর থেকে আলাদা রাখা হয়েছে।
{
roll = r; // অবজেক্টের ভেলু এসাইন করে দেওয়া হচ্ছে।
cls = c;
gpa = g;
}
};
int main()
{
Student rahim(29, 9, 5.00); // অবজেক্ট ক্রিয়েট করা হলো এবং কন্সট্রাক্টর কল করে ভেলু পাস করে দেওয়া হলো।
Student karim(45, 10, 4.33); // দ্বিতীয় অবজেক্ট ক্রিয়েট করা হলো এবং কন্সট্রাক্টর কল করে ভেলু পাস করে দেওয়া হলো।
cout << rahim.roll << " " << rahim.cls << " " << rahim.gpa << endl; // প্রথম অবজেক্ট প্রিন্ট করা হয়েছে।
cout << karim.roll << " " << karim.cls << " " << karim.gpa << endl; // দ্বিতীয় অবজেক্ট প্রিন্ট করা হয়েছে।
return 0;
}এভাবে আমরা কন্সট্রাক্টর ব্যাবহার করে এক লাইনে অবজেক্ট ডিক্লেয়ার করার সময় ভেলু এসাইন করে দিতে পারি। কিন্তু এখানে আমাদের কন্সট্রাক্টর এর মধ্যে ভেরিয়েবল এর নাম ভিন্ন দিতে হয়েছে। আমরা যদি চাই ভেরিয়েবল এর নাম সেইম দিতে তাহলে আমরা নরমালি করতে পারব না। কারন আমরা যদি লিখি,
class Student
{
public:
int roll;
int cls;
double gpa;
Student(int roll, int cls, double gpa)
{
roll = roll; // তাহলে কম্পাইলার বুঝতে পারবে না এখানে roll বলতে ক্লাস ভেরিয়েবল কে বুঝানো হচ্ছে নাকি কন্সট্রাক্টর এর ভেরিয়েবলকে বুঝানো হচ্ছে।
cls = cls;
gpa = gpa;
}
};এই কোডটি রান করলে গারবেজ ভেলু আসবে কারন কম্পাইলার বুঝবে না কোন roll কে কোন roll এর মধ্যে রাখতে হবে। তাই এখানে আমাদের একটি এক্সট্রা কি-ওয়ার্ড ব্যাবহার করতে হবে, this কি-ওয়ার্ড। এই this আসলে একটি পয়েন্টার যার মধ্যে অবজেক্ট এর এড্রেস স্টোর থাকে। তাই এই পয়েন্টার কে ডিরেফারেন্স করে আমরা ক্লাস ভেরিয়েবলগুলো পেতে পারি। (_this).roll এভাবে। শুরুতে আমরা ডিরেফারেন্স করে নিলাম _ এর মাধ্যমে। তারপর . দিয়ে ভেরিয়েবল গুলো এক্সেস করলাম। এই কাজটি শর্টকাটে এরো সাইন ( -> ) দিয়ে করে ফেলা যায়। আমরা এভাবে লিখতে পারি this->roll
#include <bits/stdc++.h>
using namespace std;
class Student
{
public:
int roll;
int cls;
double gpa;
Student(int roll, int cls, double gpa)
{
this->roll = roll; // দিস কি-ওয়ার্ড দিয়ে ক্লাস ভেরিয়েবল roll এর মধ্যে কন্সট্রাক্টর ভেরিয়েবল roll কে রেখে দেওয়া হচ্ছে।
this->cls = cls;
this->gpa = gpa;
}
};
int main()
{
Student rahim(29, 9, 5.);
Student karim(45, 10, 4.33);
cout << rahim.roll << " " << rahim.cls << " " << rahim.gpa << endl;
cout << karim.roll << " " << karim.cls << " " << karim.gpa << endl;
return 0;
}মডিউল ৩-৬ঃ ফাংশন থেকে অবজেক্ট রিটার্ন করা এবং কেন আমাদের ডায়নামিক অবজেক্ট প্রয়োজন
এবার আমরা দেখব কিভাবে ফাংশন থেকে অবজেক্ট রিটার্ন করতে হয়।
নরমালি একটি int অবজেক্ট আমরা যেভাবে ফাংশন থেকে রিটার্ন করতাম, একটি অবজেক্টও আমরা ঠিক একইভাবে ফাংশন থেকে রিটার্ন করতে পারি।
#include <bits/stdc++.h>
using namespace std;
class Student // ক্লাস ক্রিয়েট করা হলো
{
public:
int roll;
int cls;
double gpa;
Student(int roll, int cls, double gpa) // কন্সট্রাক্টর ডিক্লেয়ার করা হয়েছে।
{
this->roll = roll;
this->cls = cls;
this->gpa = gpa;
}
};
Student fun() // ফাংশন ডিক্লেয়ার করা হয়েছে যা Student ক্লাসের অবজেক্ট রিটার্ন করবে। তাই এই ফাংশনের রিটার্ন টাইপ Student
{
Student rahim(342, 5, 4.99); // কন্সট্রাক্টর কল করে অবজেক্ট ডিক্লেয়ার করা হয়েছে।
return rahim; // নরমাল int ভেরিয়েবল এর মতো অবজেক্ট রিটার্ন করা হয়েছে।
}
int main()
{
Student ans = fun(); // ফাংশন কল করা হয়েছে এবং ফাংশন থেকে রিটার্ন আসা অবজেক্টটি স্টোর রাখার জন্য আরেকটি Student ক্লাসের অবজেক্ট নেওয়া হয়েছে। ফাংশন রিটার্ন হওয়ার পর ফাংশনে থাকা অবজেক্ট ডিলিট হয়ে যাবে যেহেতু এটি ডায়নামিক অবজেক্ট নয়।
cout << ans.roll << " " << ans.cls << " " << ans.gpa << endl; // অবজেক্ট এর ভেলু প্রিন্ট করা হয়েছে।
return 0;
}এভাবে আমরা ফাংশন থেকে অবজেক্ট রিটার্ন করতে পারি।
এখন আমরা যদি চাই ফাংশন থেকে অবজেক্ট রিটার্ন করে সেই অবজেক্ট আরেকটি অবজেক্টে কপি করে না রেখে যেই অবজেক্ট ফাংশনে ছিল ওটাই পয়েন্টার এর মাধ্যমে রিটার্ন করতে তাহলে কি হতে পারে?
#include <bits/stdc++.h>
using namespace std;
class Student
{
public:
int roll;
int cls;
double gpa;
Student(int roll, int cls, double gpa)
{
this->roll = roll;
this->cls = cls;
this->gpa = gpa;
}
};
Student *fun() // এবার ফাংশনটি একটি পয়েন্টার রিটার্ন করবে, পয়েন্টার এর টাইপ Student* তাই ফাংশনের রিটার্ন টাইপও Student*
{
Student rahim(342, 5, 4.99); // কন্সট্রাক্টর কল করে অবজেক্ট ডিক্লেয়ার করা হয়েছে।
Student *p = &rahim; // একটি পয়েন্টার নিয়ে তাতে অবজেক্ট এর এড্রেস রেখে দেওয়া হয়েছে। যেহেতু পয়েন্টার একটি Student ক্লাসের অবজেক্টকে পয়েন্ট করছে তাই পয়েন্টার এর ডাটা টাইপ হচ্ছে Student*
return p; // পয়েন্টার রিটার্ন করা হচ্ছে।
}
int main()
{
Student *ans = fun(); // ফাংশন কল করে ফাংশন থেকে রিটার্ন আসা পয়েন্টার আরেকটি পয়েন্টারে স্টোর করে রাখা হচ্ছে।
cout << ans->roll << " " << ans->cls << " " << ans->gpa << endl; // সেই পয়েন্টার কে ডিরেফারেন্স করে ( এক্ষেত্রে শর্টকাটে এরো সাইন ব্যাবহার করা হয়েছে ) তার ভেলু প্রিন্ট করা হচ্ছে।
return 0;
}কোডটি রান করলে আমরা দেখতে পাব গারবেজ ভেলু প্রিন্ট হচ্ছে। কারন ফাংশন থেকে রিটার্ন হওয়ার পর ফাংশনে থাকা অবজেক্ট ডিলিট হয়ে গিয়েছে। তাই পয়েন্টার এখন যেই এড্রেসে পয়েন্ট করে আছে সেখানে কোন অবজেক্ট নেই। তাই রান করলে গারবেজ ভেলু আসছে।
আমরা যদি এভাবে পয়েন্টার দিয়ে অবজেক্ট রিটার্ন করতে চাই তাহলে আমাদের ডায়নামিক অবজেক্ট ব্যাবহার করতে হবে।
মডিউল ৩-৭ঃ সি++ এ ডায়নামিক অবজেক্ট ক্রিয়েট করা
এবার আমরা দেখব কিভাবে ডায়নামিক অবজেক্ট ক্রিয়েট করা যায়।
ডায়নামিক এরে আমরা যেভাবে ক্রিয়েট করেছিলাম, ডায়নামিক অবজেক্টও আমরা সেইমভাবে ক্রিয়েট করে ফেলতে পারি new কি-ওয়ার্ড ব্যাবহার করে।
#include <bits/stdc++.h>
using namespace std;
class Student
{
public:
int roll;
int cls;
double gpa;
Student(int roll, int cls, double gpa)
{
this->roll = roll;
this->cls = cls;
this->gpa = gpa;
}
};
Student *fun() // ফাংশনটি একটি পয়েন্টার রিটার্ন করবে, পয়েন্টার এর টাইপ Student* তাই ফাংশনের রিটার্ন টাইপও Student*
{
Student* rahim = new Student(342, 5, 4.99); // new কি-ওয়ার্ড ব্যাবহার করে Student টাইপের একটি ডায়নামিক অবজেক্ট ক্রিয়েট করা হয়েছে। ডায়নামিক অবজেক্ট পয়েন্টারে স্টোর রাখার জন্য একটি পয়েন্টার নেওয়া হয়েছে rahim নামের।
return rahim; // পয়েন্টার রিটার্ন করা হচ্ছে, যা পয়েন্ট করছে একটি ডায়নামিক অবজেক্টকে। তাই ফাংশন থেকে রিটার্ন হওয়ার পরও এই পয়েন্টার দিয়ে ডায়নামিক অবজেক্টকে এক্সেস করা যাবে।
}
int main()
{
Student *ans = fun(); // ফাংশন কল করা হয়েছে এবং ফাংশন থেকে রিটার্ন আসা অবজেক্ট এর পয়েন্টারটি স্টোর রাখার জন্য আরেকটি Student* টাইপের পয়েন্টার নেওয়া হয়েছে।
cout << ans->roll << " " << ans->cls << " " << ans->gpa << endl; // ডায়নামিক অবজেক্ট প্রিন্ট করা হচ্ছে।
delete ans; // প্রিন্ট করার পর ডায়নামিক অবজেক্ট ডিলিট করে দেওয়া যেতে পারে।
return 0;
}এভাবে আমরা একটি ক্লাসের ডায়নামিক অবজেক্ট ক্রিয়েট করে ফেলতে পারি যা ফাংশন থেকে রিটার্ন হয়ে গেলেও অটোমেটিক ডিলিট হয়ে যাবে না।
মডিউল ৩-৮ঃ বিল্ট-ইন সর্ট ফাংশন ব্যাবহার করে এরে সর্ট করা
আমরা সি কোর্সে সিলেকশন সর্ট দেখেছি। আজকে আমরা সি++ এর বিল্ট-ইন সর্ট ফাংশন দেখব।
সি++ এর বিল্ট-ইন সর্ট ফাংশন এর সিন্টেক্সঃ
sort( starting_pointer, ending_pointer);এই সিন্টেক্সে কোড লিখলে এরে এসেন্ডিং অর্ডারে ছোট থেকে বড় সর্ট হয়ে যাবে। এখানে আমাদের দুটি পয়েন্টার দিতে হয়, স্টারটিং এবং এন্ডিং পয়েন্টার। এরেতে কোথা থেকে শুরু করে কোন পর্যন্ত সর্ট করতে চাই তা বলে দিতে হয়। আমরা যদি এরের শুরু থেকে সর্ট করতে চাই তাহলে আমাদের এরের প্রথম ইন্ডেক্স এর এড্রেস দিতে হয়। আমরা জানি এরের নাম টিই আসলে একটি পয়েন্টার যাতে এরের প্রথম ইন্ডেক্স এর এড্রেস থাকে। তাই স্টারটিং পয়েন্টার হিসেবে আমরা এরের নামটি দিয়ে দিতে পারি। এন্ডিং পয়েন্টার হিসেবে যেই ইন্ডেক্স এর এড্রেস দেওয়া হবে সর্ট ফাংশন তার পূর্ববর্তী ইন্ডেক্স পর্যন্ত সর্ট করে দিবে। আমরা যদি এরের ৪ নম্বর ইন্ডেক্স এর এড্রেস দেই তাহলে ৩ নম্বর ইন্ডেক্স পর্যন্ত সর্ট হবে। আমরা যদি সম্পূর্ণ এরে সর্ট করতে চাই তাহলে এরে নাম + সাইজ দিয়ে দিতে পারি। কারন একটি এরেতে ০ থেকে সাইজ-১ পর্যন্ত ইন্ডেক্স থাকে। এন্ডিং পয়েন্টার হিসেবে এরে নাম + সাইজ দিলে সাইজ-১ পর্যন্ত সর্ট হবে।
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin >> n; // সাইজ ইনপুট নেওয়া হচ্ছে।
int a[n]; // সেই সাইজের এরে ডিক্লেয়ার করা হচ্ছে।
for (int i = 0; i < n; i++)
{
cin >> a[i]; // এরে ইনপুট নেওয়া হচ্ছে।
}
sort(a, a + n); // সর্ট ফাংশনের মধ্যে স্টারটিং পয়েন্টার হিসেবে এরের প্রথম ইন্ডেক্স এবং এন্ডিং পয়েন্টার হিসেবে এরের সাইজ দিয়ে দেওয়া হচ্ছে। তাহলে ০ ইনডেক্স থেকে সাইজ-১ ইন্ডেক্স পর্যন্ত সর্ট হবে অর্থাৎ সম্পূর্ণ এরেটি সর্ট হবে। আমরা যদি a+3 লিখতাম তাহলে ০ ইন্ডেক্স থেকে ২ ইনডেক্স পর্যন্ত সর্ট হতো।
for (int i = 0; i < n; i++)
{
cout << a[i] << " "; // এরে প্রিন্ট করা হচ্ছে।
}
return 0;
}আমরা এভাবে বিল্ট-ইন সর্ট ফাংশন ব্যাবহার করে যেকোন এরে ছোট থেকে বড় সর্ট করে ফেলতে পারি। আমরা চাইলে ডিসেন্ডিং অর্ডারেও (বড় থেকে ছোট) সর্ট করতে পারি। সেক্ষেত্রে এভাবে লিখতে হবেঃ
sort( starting_pointer, ending_pointer, greater<array_data_type>());
এখানে আমরা একটি কম্পেয়ার ফাংশনকে কল করেছি এবং তার মধ্যে এরের ডাটা টাইপ দিয়ে দিয়েছি। আমরা ডাটা স্ট্রাকচার কোর্সে কম্পেয়ার ফাংশন নিয়ে বিস্তারিত জানব।
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin >> n;
int a[n];
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
sort(a, a + n, greater<int>()); // সর্ট ফাংশনের মধ্যে স্টারটিং পয়েন্টার হিসেবে এরের প্রথম ইন্ডেক্স এবং এন্ডিং পয়েন্টার হিসেবে এরের সাইজ দিয়ে দেওয়া হচ্ছে। বড় থেকে ছোট অর্ডারে সর্ট করতে চাই তাই greater ফাংশনকে কল করা হয়েছে তার মধ্যে এরের ডাটা টাইপ int দিয়ে দেওয়া হয়েছে।
for (int i = 0; i < n; i++)
{
cout << a[i] << " ";
}
return 0;
}কোডটি রান করলে দেখব এরেটি বড় থেকে ছোট অর্ডারে সর্ট হয়ে গিয়েছে।
