16 Ekim 2017 Pazartesi

STL Veri Yapısı - Vector Eleman Ekleme Metodları

emplace
Bu metod C++11 ile geldi. Aynı emplace_back() gibidir. Tek fark olarak yeni nesne en sona değil verilen iterator'den sonra eklenir.
struct Foo
{
  Foo(int n, double x);
};

std::vector<Foo> v;
v.emplace(someIterator, 42, 3.1416);
emplace_back metodu
Bu metod C++11 ile geldi. emplace_back() metodu verilen parametreleri alarak, vector'ün kendi içinde bir nesne oluşturur. Yani nesnenin constructor metodunu kullanarak aşağıdaki gibi kod yazmaya gerek kalmaz.
v.push_back(Foo(42, 3.1416));
Bunun yerine emplace_back() kullanılır. Elimizde Foo vector'ü olsun. Bu vector'e emplace_back ile şöyle ekleme yaparız.
struct Foo
{
  Foo(int n, double x);
};

std::vector<Foo> v;
v.emplace_back(42, 3.1416);
std::string için şöyle yaparız.
v.emplace_back("some_string");
emplace_back boş çağrılabilir
Eğer nesnemiz parametre almıyorsa sadece emplace_back()'i çağırmamız yeterli.
v.emplace_back();
emplace_back lvalue için çağrılabilir
Şöyle yaparız.
std::string str("some_string");
v.emplace_back(std::move(str));
emplace_back move constructor gerektirir
Açıklaması şöyle
In order to emplace into a vector, the element type must be MoveInsertable and MoveAssignable.
Emplace metodlarının çalışabilmesi için nesnenin move constructor metodunun olması gerekir. Eğer move constructor sağlamıyorsak veya move constructor üretilmesi engelleniyorsa derleyici hata verir.
Örnek
Aşağıdaki kod parçası derlenmez çünkü copy constructor delete edilse bile sağlanıyor ve move constructor'ın üretilmesi engelleniyor.
class MyType {
public:
    MyType(std::array<double, 6> a) {}
    MyType(const MyType& that) = delete;
};

int main() {
    std::vector<MyType> v;
    std::array<double, 6> a = {1,2,3,4,5,6};
    v.emplace_back(a);
}
Dolayısıyla şöyle bir move constructor sağlamamız gerekir.
MyType(MyType &&a) {/*code*/} //move constructor
Örnek
Aşağıdaki kod parçası derlenmez çünkü copy constructor sağlanmış ancak const değil! Derlenmesi için copy constructor const yapılır veya yapılmak istenmezse move constructor tanımlanır.
struct Vertex
{
  Vertex(float pos) { }
  Vertex(Vertex& other) { }
};

std::vector<Vertex> arrayOfVertices;
arrayOfVertices.emplace_back(7.f);
emplace_back'in kendi elemanı ile çağrılması
Elimimizde şöyle bir kod olsun. Bu kod sorunsuz çalışır.
std::vector<int> nums{21, 22, 23, 24};
nums.emplace_back(nums[0]);
nums.emplace_back(nums[1]);

for (auto n : nums) {
    std::cout << n << std::endl;
}
Kendi elemanımız ile kullandığımız için eğer vector resize olursa elemanda bozulmalar olabilir diye düşünebiliriz. Ancak standart bu durum için şöyle bir çalışma kuralı getiriyor.
1. Önce yeni bellek ayrılır
2. emplace_back yeni bellek alanı üzerinde gerçekleşir.
3. Mevcut elemanlar yeni bellek alanına taşınır.
4. Eski bellek silinir.

insert metodu
Açıklaması şöyle. Metod vector'ün boyunu büyütür. 
The vector is extended by inserting new elements before the element at the specified position, effectively increasing the container size by the number of elements inserted.
This causes an automatic reallocation of the allocated storage space if -and only if- the new vector size surpasses the current vector capacity. 
insert metodu vector veri yapısının vermiş olduğu tüm iterator ve reference'ları geçersiz hala getirir.
Metodun C++98 imzası şöyledir.
void insert (iterator position, size_type n, const value_type& val);
C++11 ile imzası ise şöyle
iterator insert(const_iterator position, const T& x);
iterator insert(const_iterator position, T&& x);
Iterator invalidation rules yazısı durumu özetliyor.
all iterators and references before the point of insertion are unaffected, unless the new container size is greater than the previous capacity (in which case all iterators and references are invalidated)
push_back metodu
Yeni C++11 ile push_back metodları iki imzaya sahip oldular. Bunlar
vector<T>::push_back(const T&);
ve move işlemi ile çalışan
vector<T>::push_back(T&&);
metodları.

move ile çalışan metodun için şuna benzer.
void push_back( Object && x )
{
    if( theSize == theCapacity )
        reserve( 2 * theCapacity + 1 );
    objects[ theSize++ ] = std::move( x );
}
Bu metodun iki tane önemli özelliği var. Birinci özelliği metoda geçilen parametrenin kopyasını alınır.
Adds a new element at the end of the vector, after its current last element. The content of val is copied (or moved) to the new element.
İkinci özelliği ise metodun kullanılmakta olan iterator'leri gerçersiz hale (invalidate) getirmesi.
Burada ilginç bir soru var. Vektör'ün içindeki bir elemanı tekrar push_back() ile eklersek ne olur diye soruyor.

exception
Açıklaması şöyle
If an exception is thrown (which can be due to Allocator::allocate() or element copy/move constructor/assignment), this function has no effect (strong exception guarantee).
Açıklaması şöyle
So in case of failure, the last push_back which caused the exception will be rolled back, but everything else will be fine: your vector will contain all of the previously pushed elements, and will be in a consistent state.
Elimizde şöyle bir kod olsun. Eğer push_back exception atarsa v exception fırlatan en son push_back işleminden önceki halinde kalır.
std::vector<int> v;
try {
  for(unsigned int i = 0; i < desiredSize; ++i)
    v.push_back(i);
}
catch (const std::bad_alloc&) {
  cerr << "Out of memory! v.size() = " << v.size() << endl;
}




Hiç yorum yok:

Yorum Gönder