Phân Tích Các Hàm Xử Lý Bộ Nhớ Trong STL

Thư viện STL cung cấp một tập hợp các hàm toàn cục trong tệp <bits/stl_uninitialized.h> để hỗ trợ khởi tạo bộ nhớ cho các container. Bài viết này phân tích chi tiết cơ chế hoạt động của các hàm này.

Hàm __uninitialized_default_n_a

Hàm này khởi tạo một dải phần tử bằng constructor mặc định.

template<typename Iter, typename Size, typename Allocator>
Iter __uninitialized_default_n_a(Iter start, Size count, Allocator& alloc) {
    Iter current = start;
    try {
        for (; count > 0; --count, ++current) {
            std::allocator_traits<Allocator>::construct(alloc, &*current);
        }
        return current;
    } catch(...) {
        std::_Destroy(start, current, alloc);
        throw;
    }
}

Phiên bản chuyên biệt cho bộ cấp phát std::allocator:

template<typename Iter, typename Size, typename T>
Iter __uninitialized_default_n_a(Iter start, Size count, std::allocator<T>&) {
    return std::__uninitialized_default_n(start, count);
}

Hàm __uninitialized_default_n xác định chiến lược khởi tạo dựa trên tính chất của kiểu dữ liệu:

template<typename Iter, typename Size>
Iter __uninitialized_default_n(Iter start, Size count) {
    using ValueType = typename std::iterator_traits<Iter>::value_type;
    bool can_fill = std::is_integral<Size>::value && std::is_copy_assignable<ValueType>::value;
    return __dispatch_default_n<std::is_trivial<ValueType>::value && can_fill>::execute(start, count);
}

Chiến lược khởi tạo được chia thành hai trường hợp:

template<bool IsTrivial>
struct __dispatch_default_n {
    template<typename Iter, typename Size>
    static Iter execute(Iter start, Size count) {
        Iter cur = start;
        try {
            for (; count > 0; --count, ++cur) {
                std::_Construct(&*cur);
            }
            return cur;
        } catch(...) {
            std::_Destroy(start, cur);
            throw;
        }
    }
};

template<>
struct __dispatch_default_n<true> {
    template<typename Iter, typename Size>
    static Iter execute(Iter start, Size count) {
        if (count > 0) {
            auto* first_elem = &*start;
            std::_Construct(first_elem);
            ++start;
            start = std::fill_n(start, count - 1, *first_elem);
        }
        return start;
    }
};

Hàm __uninitialized_fill_n_a

Hàm này khởi tạo dải phần tử bằng cách sao chép từ một giá trị cho trước.

template<typename Iter, typename Size, typename T, typename Allocator>
Iter __uninitialized_fill_n_a(Iter start, Size count, const T& val, Allocator& alloc) {
    Iter cur = start;
    try {
        for (; count > 0; --count, ++cur) {
            std::allocator_traits<Allocator>::construct(alloc, &*cur, val);
        }
        return cur;
    } catch(...) {
        std::_Destroy(start, cur, alloc);
        throw;
    }
}

Logic lựa chọn giữa constructor và phép gán:

template<typename Iter, typename Size, typename T>
Iter uninitialized_fill_n(Iter start, Size count, const T& val) {
    using ValueType = typename std::iterator_traits<Iter>::value_type;
    bool use_assign = __use_assign_for_init<ValueType, const T&>() && std::is_integral<Size>::value;
    return __dispatch_fill_n<use_assign>::execute(start, count, val);
}

Hàm __uninitialized_copy_a

Hàm này sao chép một dải phần tử sang vùng nhớ chưa được khởi tạo.

template<typename InputIter, typename OutputIter, typename Allocator>
OutputIter __uninitialized_copy_a(InputIter src_begin, InputIter src_end, OutputIter dest, Allocator& alloc) {
    OutputIter cur = dest;
    try {
        for (; src_begin != src_end; ++src_begin, ++cur) {
            std::allocator_traits<Allocator>::construct(alloc, &*cur, *src_begin);
        }
        return cur;
    } catch(...) {
        std::_Destroy(dest, cur, alloc);
        throw;
    }
}

Quyết định sử dụng memmove hoặc phép gán:

template<typename InputIter, typename OutputIter>
OutputIter uninitialized_copy(InputIter src_begin, InputIter src_end, OutputIter dest) {
    using SrcType = typename std::iterator_traits<InputIter>::value_type;
    using DstType = typename std::iterator_traits<OutputIter>::value_type;
    bool use_memmove = std::is_trivial<SrcType>::value;
    bool assignable = __use_assign_for_init<DstType, decltype(*src_begin)>();
    return __dispatch_copy<use_memmove && assignable>::execute(src_begin, src_end, dest);
}

Hàm __uninitialized_move_a

Hàm này thực hiện di chuyển (move) hoặc sao chép các phần tử.

template<typename InputIter, typename OutputIter, typename Allocator>
OutputIter __uninitialized_move_a(InputIter src_begin, InputIter src_end, OutputIter dest, Allocator& alloc) {
    return __uninitialized_copy_a(std::make_move_iterator(src_begin),
                                  std::make_move_iterator(src_end),
                                  dest, alloc);
}

Tổng Kết Chiến Lược Khởi Tạo

  • __uninitialized_default_n_a: Sử dụng constructor mặc định. Với kiểu dữ liệu tầm thường (trivial) và có thể gán, tối ưu bằng phép gán hoặc memset.
  • __uninitialized_fill_n_a: Sử dụng constructor sao chép. Tối ưu tương tự với kiểu dữ liệu phù hợp.
  • __uninitialized_copy_a: Sao chép giữa hai dải. Tối ưu bằng memmove nếu kiểu dữ liệu cho phép, ngược lại dùng constructor sao chép.
  • __uninitialized_move_a: Tương tự sao chép nhưng hỗ trợ ngữ nghĩa di chuyển trong C++11 trở lên.

Các hàm này đều cung cấp cơ chế an toàn ngoại lệ (exception safety), hủy các đối tượng đã được khởi tạo khi xảy ra lỗi.

Thẻ: STL C++ Memory Management Uninitialized Algorithms Allocators template metaprogramming

Đăng vào ngày 1 tháng 9 lúc 19:20