7. vector<T>
When you insert a new element, it’s value is
copied into the container
8. 01. void silly(vector<Entry>& book)
02. {
03. // book.size() is out of range
04. int i = book[ph.size()].number;
05. // ...
06. }
01. template<typename T>
02. class Vec : public std::vector<T> {
03. public:
04. // use the constructors from vector (under the name Vec)
05. using vector<T>::vector;
06. T& operator[](int i) // range check
07. { return vector<T>::at(i); }
08. const T& operator[](int i) const // range check const objects
09. { return vector<T>::at(i); }
10. }
9. 01. list<Entry> phone_book = {
02. {"David Hume",123456},
03. {"Karl Popper",234567},
04. {"Bertrand Arthur William Russell",345678}
05. };
01. int get_number(const string& s)
02. {
03. for (const auto& x : phone_book)
04. if (x.name==s)
05. return x.number;
06. return 0; // use 0 to represent "number not found"
07. }
10. 01. int get_number(const string& s)
02. {
03. for (auto p = phone_book.begin(); p!=phone_book.end(); ++p)
04. if (p–>name==s)
05. return p–>number;
06. return 0; // use 0 to represent "number not found"
07. }
11. 01. int get_number(const string& s)
02. {
03. for (auto p = phone_book.begin(); p!=phone_book.end(); ++p)
04. if (p–>name==s)
05. return p–>number;
06. return 0; // use 0 to represent "number not found"
07. }
01. void f(const Entry& ee, list<Entry>::iterator p
02. , list<Entry>::iterator q)
03. {
04. // add ee before the element referred to by p
05. phone_book.insert(p, ee);
06. // remove the element referred to by q
07. phone_book.erase(q);
08. }
Editor's Notes
توی این دوباره یه اشاره ای به مفهوم container می کنم و ویدیو وکتور و لیست رو که دوتا از پر کاربرد ترین containerهای کتابخونه استاندارد رو معرفی میکنم.
برنامه هایی که می نویسیم معمولا شامل یه مجموعه از مقادیری مشن که ما اونا رو ایجاد می کنیم و یه collection از اونا رو درست می کنیم و بعد روی اونا عملیاتهایی رو انجام می دیم. مثال کار کردن با string و خوندن کاراکتر ها از روی ورودی و چاپ کردن مقدار اون یه مثال ساده هست.
در ویدیوی شماره 7 در مورد مفهوم containerها صحبت کرده بودیم. هر کلاسی که یه مجموعه از Objectها رو نگه داری کنه بهش می گیم Container. برای اینکه یه کاری رو انجام بدیم باید container مناسب اون کار رو به همراه عملیاتهای پایه ای روی Container رو باید داشته باشیم.
برای اینکه روشن تر بشه فرض کنید که ما می خوایم یه دفترچه تلفن درست کنیم که شامل یه سری نام و شماره تلفن اون نام هست. ما باید طوری این برنامه رو بنویسیم که واضح و خوانا باشه و هر کسی با هر پیش زمینه ای بتونه از اون سر در بیاره. Structی Entry که در ویدیوی قبلی دیدیم می تونه برای نگه داری یه رکورد توی دفترچه تلفن استفاده بشه. توی اون struct خیلی از پیچیدگی های دنیای واقعی رو در نظر نمی گیریم مثلا تلفن های واقعی توی int جا نمی شن ولی برای سادگی ما شماره تلفن رو int در نظر گرفتیم. مثال دفترچه تلفن توی تمام این فصل برای توضیح دادن containerها استفاده می شه.
مفیدترین container در کتابخونه استاندارد وکتوره. وکتور یه sequence از المانهاست که پشت سر هم و به صورت پیوسته در حافظه قرار گرفتند. در مورد وکتور تا حالا خیلی بحث کردیم و مثال زدیم.
ما میتونیم وکتور را با یه set از eleman typeهایی که داره initialize کنیم . توی این مثال یه وکتور از entryها به نام phone_book ساختیم و با 3 تا عنصر مقدار دهی کردیم.
با اپراتور subscript به عناصر اون دسترسی داشته باشیم. Indexها از صفر شروع میشن یعنی book[0] برابر رکورد david hume خواهد بود.
وکتور یه تابع به نام size داره که تعداد المانهای داخل اون رو بر میگردونه. ما از size توی for برای شرط خروج حلقه استفاده کردیم.
ما می تونیم از range for برای دسترسی به المانهای وکتور استفاده کنیم. با range for به تک تک عناصر وکتور دسترسی داریم و می تونیم عملیات انجام بدیم. با استفاده از auto می تونیم type المانهای وکتور رو ننویسیم و کامپایلر خودش به صورت هوشمند این کار رو می کنه.
هر وقت یه وکتور تعریف می کنیم size اون بر اساس المانهایی که برای initializeکردن بهش می دیم مقدار می گیره.
اینجا چون 4 تا المان به عنوان initializer list به وکتور دادیم پس سایز اولیه اون برابر 4 میشه.
اما اینجا وکتور initialize نشده پس مقدار size برابر صفر خواهد شد.
این خط با استفاده از کانستراکتور تک ورودی یه وکتور با 23 تا المان میسازه که مقادیر اولیه اون المانها با nullptr مقدار دهی می شن. اگر برای المانهای وکتور مقدار اولیه تعیین نشه default constructor اون المانها اجرا خواهد شد.
و اینجا با کانستراکتور 2 ورودی 32 تا المان میسازه که با 9.9 مقدار اولیه داده میشن. مقدار اولیه default رو با دادن پارامتر دوم به وکتور میشه تغییر دار و بجای اینکه default constructor اجرا بشه با این پارامتر مقدار دهی میشه.
یکی از فیچر های مهم وکتور اینه که بر خلاف آرایه که سایزش غیرقابل تغییره ، تعداد عناصر داخل vector رو میشه تغییر داد. با تابع push_back میشه یه المان رو به انتهای وکتور اضافه کرد.
تابع input از ورودی یه تعداد entry میخونه و بعد اونارو به انتهای وکتور اضافه میکنه. زمانی این حلقه تموم میشه که به انتهای ورودی برسیم و یا فرمت بندی ورودی اشتباه باشه. Vector طوری پیاده سازی شده که با push_back کردن المانهای جدید، رشد میکنه و بزرگ میشه تا جا برای المانهای جدید هم باز بشه. این بزرگ شدن وکتور رو طوری پیاده سازی کردند که تا حد قابل قبولی efficient باشه.
یه وکتور جدید رو میشه با assignment , initialize کرد. چون copy و move constructor برای وکتور تعریف شده و میشه با Move و Copy کردن وکتور رو initialize کرد. ولی زمانی که وکتور تعداد زیادی المان داخلش باشه copy هزینه ی زیادی داره و باید از move کمک گرفت یا اینکه از pointer یا reference استفاده کرد.
توی این اسلاید در مورد چند تا نکته مهم در مورد المانهای وکتور اشاره میکنم.
وکتور و همه ی containerها کتابخونه استاندارد به صورت templateیی پیاده سازی شده و type المان های اون پارامتری تعیین میشه. پارامتر T میتونه هر چیزی مثل built-in typeها یا Pointerیا user-defined type ها باشه.
وقتی یه المان جدید به وکتور push_back میشه value اون copyمیشه. برای المان های کوچیک مثل اعداد صحیح این کپی کردن هزینه ای نداره ولی اگه اندازه المان ها بزرگتر بشه هزینه ی کپی کردن بالا میره. این نکته ایه که وقتی از وکتور استفاده میکنید باید مد نظر داشته باشید.
یه نکته هم در مورد اپراتور subscript توی این اسلاید میگم و اون اینکه وکتور هیچ تضمینی برای چک کردن دسترسی های out of range یا خارج از محدوده نداره.
برای اینکه این مشکل حل بشه میتونیم یه کلاس به این صورت بنویسم که از وکتور مشتق شده باشه و اپراتور های subscript اون رو باز نویسی کنیم. تابع at یه تابعی هست که برای پیاده سازی subscript استفاده شده. این تابع بر خلاف اپراتور subscript اگر خارج از محدوده بخوایم ازش استفاده کنیم exception از نوع Out_of_range میده. برای اینکه این exception رو catch کنید باید از try catch استفاده کنید که توی ویدیوی شماره 6 با هم دیده بودیم.
بعد از وکتور، توی این اسلاید لیست رو بررسی می کنیم. کتابخونه استاندارد لیست دوطرفه هم برای ما فراهم کرده. از لیست برای نگهداری یه sequence از المان هاست. توی لیست insert و delete کردن المانها اون بدون جابجا کردن بقیه المانها توی حافظه امکان پذیره. برای initialize کردن مثل vector از initializer list استفاده می کنیم.
اما بر خلاف وکتور برای دسترسی به المانها نمی تونیم از subscript استفاده کنیم و باید کل لیست رو برای پیدا کردن المانی که مورد نظرمون هست search کنیم. برای search کردن باید از اولین المان داخل لیست شروع کنیم و تا زمانی که المان رو پیدا نکردیم یا به انتهای لیست نرسیدیم ادامه میدیم. برای پیمایش مثل همه ی containerها می تونیم از range for استفاده کنیم.
ما میتونیم بجای استفاده کردن از range for که در مثال قبل دیدیم از iterator برای پیمایش لیست استفاده کنیم. می تونیم تابع get_nubmer مثال قبل رو به این صورت بازنویسی کنیم.
با استفاده از begin یه interator به ابتدای لیست می گیریم
و با استفاده از end چک می کنیم که به انتهای لیست رسیدیم یا نه
اگر به انتها نرسیدیم؛ مقدار iterator رو یکی اضافه میکنیم. با ++ کردن یه iterator به المان بعدی در اون container اشاره خواهد کرد.
نحوه ی پیاده سازی iterator به صورتی هست که با اون مثل یه pointer باید رفتار کرد. یعنی با اپراتور فلش میشه به اعضای اون المان دسترسی داشت و یا به dereference کردن iterator به خود اون المان دسترسی داشت.
با استفاده از Iterator ها میشه کار های بیشتری انجام داد.
در اینجا با استفاده از متد insert میتونیم یه عنصر رو به لیست اضافه کنیم. با داشتن یه iterator میتونیم یه entry رو قبل از اون iterator در لیست قرار بدیم.
و با داشتن Iterator می تونیم اون المان رو با استفاده از متد erase از لیست حذف کنیم.
اما تفاوت لیست و وکتور چیه. تفاوت اونا توی پیاده سازی اوناست. وکتور های کوچیک با تعداد المانهای کم بهتر از لیست عمل می کنن ولی وقتی تعداد المان ها زیاد میشه و تعداد insert و delete ها زیاد میشه، لیست خیلی بهتر از وکتوره. زمانی که شما به یه sequence از المان ها نیاز دارید باید بین وکتور و لیست یکی رو انتخاب کنید. Performance وکتور برای یه سری از عملیات های پیمایشی مثل findو countو عملیاتهای جستجو و مرتب سازی مثل sort و binary_serach بهتره ولی در لیست هزینه درج و حذف المانها کمتره. این نکات رو در استفاده از این 2 تا container باید در نظر داشته باشید.
در انتها یه جمع بندی بکنیم.
این ویدیو در مورد 2 تا از پر کاربردترین containerها کتابخونه استاندارد یعنی وکتور و لیست صحبت کردیم و امکانات اونا رو بررسی کردیم. و نکاتی که در مورد استفاده از اونا باید در نظر گرفت رو هم گفتیم.