11 Ekim 2017 Çarşamba

STL Veri Yapısı - Vector Constructor Metodları

Tanımlama - C++11
Template yöntemi 1988 yılında dile dahil edildi. Şöyle yaparız.
std::vector<std::vector<int>> // does not work until C++11
Tanımlama - Neden <>
Açıklaması şöyle.
The <…> brackets are used in preference to the parentheses (…) partly to emphasize the different nature of template arguments (they will be evaluated at compile time) and partly because parentheses are already hopelessly overused in C++. …
Eğer parantez kullanılsaydı şu tür okuması zor kodlar ortaya çıkardı.
template(int sz = 20) class buffer {
    buffer(int i = 10);
    // ...
};
buffer b1(100)(200);
buffer b2(100);      // b2(100)(10) or b2(20)(100)?
buffer b3;           // legal?
Constructor - Fill
Vector içinde kaç tane eleman olması istediğimizi belirtiriz. İkinci parametreyi kullanmadığımız için default constructor 10 defa çağrılır.
std::vector<T> v (10);
C++11 ile fill constructor verilen sayı kadar default constructor'ı çağırarak nesne dolduruyor.

[C++11: 23.3.6.2]:
 explicit vector(size_type n);
  1. Effects: Constructs a vector with n value-initialized elements.
  2. Requires: T shall be DefaultConstructible.
  3. Complexity: Linear in n.
Elimizde şöyle bir kod olsun.
#include <iostream>
#include <string>
#include <vector>
class C{
    public:
    C(){
        std::cout << "constructor\n";
    }
    C(C const&){
        std::cout << "copy/n";
        }
    };
int main()
{
    std::vector<C> v(10);

}
C++11 ile 10 defa şu çıktıyı alırız.
constructor 
constructor 
constructor 
constructor
constructor
constructor 
constructor
constructor
constructor
constructor
C++98 ile ve daha  eski kodlarda verilen nesneyi 10 defa kopyalamaya çalışan vector kodları da mevcut. Şu çıktıyı görürüz.
constructor
copy
copy
copy
copy
copy
copy
copy
copy
copy
copy
Not : Eğer vector için bellek ayrılsın ancak içinde eleman olmasın istiyorsak empty container constructor ile vector kurulduktan sonra, reserve() metodu ile bellek ayırmak gerekir.

Constructor - Range
Vector veriyapısını bir InputIterator vererek doldurmak mümkün. Metodun imzası şöyle.
template< class InputIt >
vector( InputIt first, InputIt last, 
        const Allocator& alloc = Allocator() );
Bu metod farklı bir container'dan veri kopyalamak veya kısmı aralığı kopyalamak için işe yarar. Eğer vector sınıfına T verilmezse InputIterator sınıfının T parametresi otomatik olarak kullanılır.

Örnek
Farklı bir container veya kısmı aralık kullanana bazı örnekler şöyle.
int a[] = {1,2,3,4,5};
std::set<int> s{3, 911};
std::vector<int> v0{1,2,3,4,5};

std::vector<int> v1(std::begin(a), std::end(a)); //Farklı container
std::vector<int> v2(a+1, a+3); //Kısmi aralık
std::vector<int> v3(s.begin(), s.end()); //Farklı container
vector<int> v4(v0.begin(), v0.begin() + 3); //Kısmi aralık
Burada dikkat edilmesi gereken şey, InputIterator ile vector'ün sakladığı veri tipleri farklı olabilirler. Yani vector<int> (iterator<short>) şeklinde de kullanılabilirdi.

Örnek
Elimizde kopyalanamayan bir nesne olsun
struct Item
{
  Item(double) {}
  Item(const Item&) = delete;
  Item(Item&&) = delete;
};
Bu nesneden bir başka vector içinde yaratmak için şöyle yaparız.
std::vector<double> init(10, 3.14);
std::vector<Item> vv(init.begin(), init.end());

Constructor - Initializer List
İmzası şöyle
vector(std::initializer_list<T> init, const Allocator& alloc = Allocator());
Şöyle yaparız.
std::vector<int> v {42};
Copy Constructor metodu
Metodun içi şöyle
vector(const vector& __x)
  : _Base(__x.size(),
    _Alloc_traits::_S_select_on_copy(__x._M_get_Tp_allocator()))
{
  this->_M_impl._M_finish =
  std::__uninitialized_copy_a(__x.begin(), __x.end(),
                              this->_M_impl._M_start,
                              _M_get_Tp_allocator());
}
Kaynak vector'ün tüm elemanları kopyalanır. Kaynak vector'ün kapasitesi kopyalanmak zorunda değildir.  Şöyle yaparız.
std::vector<int> a;
a.reserve(65536);
std::vector<int> b(a);  //NOTE: b is constructed from a

a.reserve(65536); // no reallocation
b.reserve(65536);
Kaynak vector ile hedef vector aynı T tipini kullanmalıdır. Elimizde kaynak vector olsun
std::vector<std::shared_ptr<T>>
Hedef vector ise const T olsun
std::vector<std::shared_ptr<const T>>
Şöyle yaparsak hata alırız.
std::shared_ptr<int>> source = ...;
std::vector<std::shared_ptr<const int>> dest (source);
Hata şöyledir.
error: no matching constructor for initialization of 
  'std::vector<std::shared_ptr<const int> >'
Vector şöyle bir metod sağlasaydı sorun olmazdı.
template<class T, class A = std::allocator<T>>
class vector {
public:
    template<class T1, class A1>
    explicit vector(const vector<T1, A1>& other)
        : vector(other.begin(), other.end())
    {}

    ...
};
Çözüm basit. Küçük bir container_cast metodu ile şu hale getirebiliriz.
const std::vector<int> vi {1, 2, 3, 4, 5};
const std::vector<double> vd = container_cast(vi);
Şöyle yaparız.
dest (container_cast(source))

Hiç yorum yok:

Yorum Gönder