概述
文章目录
- C++ Primer learning notes
- Introduction
- 10. Generic algorithm
- lambda
- 12. dynamic memory
- shared_ptr<>
- unique_ptr<>
- weak_ptr<>
- allocator
- 13. Copy, Assign and Destroy
- specific member function to control above three operations
- copy constructor
- copy-assignment constructor
- stop the copy
- destructor
- principle
- copy control and resource management
- classes which behave like a value
- classes which behave like a pointer
- swap
- move constructor
- move assignment constructor
- reference qualifier
C++ Primer learning notes
Introduction
I write down this succession of notes for the purpose of developing my English ability&habit while learning C++. Therefore I make all the notes in English.
If you have any reap or advice, don’t hesitate to comment(with any language). If you find some bugs in my notes or sentences or grammar, please let me know(granted my English is very poor). If you are not used to reading notes in English, just close this page and browse another note.
10. Generic algorithm
lambda
[capture list1] (parameter list2) -> return type {function body}
we can ignore the parameter list and return type, but the capture list and the function body must be reserved
eg:
[](const string &a, const string &b)
{return a.size() < b.size();}
12. dynamic memory
shared_ptr<>
use function make_shared<>()
unique_ptr<>
must be initialized once the pointer is created
weak_ptr<>
can be assigned
allocator
allocator<string> alloc;
auto const p = alloc.allocate(n);
weak_ptr<string[]> q = p;
for(int i = 0; i != n; ++ i){
alloc.construct(q++, "hello world!");
}
for (int i = 0; i != n; ++i) {
cout << *(p + i) << endl;
}
alloc.deallocate(p,n);
delete p; //provided p points to an object or be nullptr
delete [] p; //provided p points to an array or be nullptr
13. Copy, Assign and Destroy
specific member function to control above three operations
copy constructor
copy-assignment operator
move constructor
move-assignment operator
destructor
copy constructor
class Foo {
public:
/*
*default defination in class body is inline
*if we don't hope so, we need to define "= default" out of the class body
*generally, function defined in class body is inline
*/
Foo() = default; //default constructor
Foo(const Foo&); //copy constructor
//first parameter must be a reference, and almost always be a const reference
}
class Foo2{
public:
//explicit prevents implicit conversion
explicit Foo2() = default;
explicit Foo2(int num): n(num) {}
private:
int n;
};
copy-assignment constructor
class Foo {
public:
/*
*we define a copy-assignment constructor below
*keyword operator+symbol is an overloaded operator
*which defines an operator(like =,+,- and so on)
*/
Foo &operator=(const Foo& f){
this->n = f->n;
return *this;
}
private:
int n;
}
int main(){
Foo f1, f2(); //f2 is default initialized
f1 = f2; //equals to f1(f2)
};
stop the copy
if we don’t want a copy operation, we need to define it as the delete function
class NoCopy {
public:
NoCopy() = default;
/*
*"= delete" must be present when we firstly declare it.
*
*by the way, we can delete any functions(except the destructor),
*thongh i haven't clearly understood its effect.
*one interesting and meaningful example is, if a class's member
*can't be implicitly initialized, copied, assigned or destroied,
*its implicit initializer's member function is deleted.
*It prevents us from creating indestructible objects.
*
*principle: destructor can't be delete
*/
NoCopy(const NoCopy &) = delete; //stop the copy
NoCopy &operator=(const NoCopy &) = delete; //stop the copy-assignment
~NoCopy() = default;
/*
*we can put the copy constructor into a private zone to prevent us from copying it.
*we need to declare it in the private zone first
*then define it in the public zone.(in order to prevent friends use it)
*however, this practice is not recommended.
*/
}
destructor
destructor deletes the resources used by objects, and destroys non-static elements
it is used whenever the object is destroyed
implicitly destruct a pointer won’t delete the object it points.
on the contrary, smart pointer is class type with destructor, thus it can delete the object
class Foo {
public:
~Foo() = default; //destructor.this function can't be overloaded
};
principle
- define destructor before assuring weather it needs a destructor or not
- if a class needs a copy constructor, it always need a copy-assignment constructor. And vice verse
copy control and resource management
behave like a value: the copy and the original object is fully independent
behave like a pointer: the copy and the original object point to the same underlying data
classes which behave like a value
class HasPtr {
public:
HasPtr(const std::string &s = std::string()):
ps(new std::string(s)), i(0) {}
//every HasPtr's copy has its own string member with equal value
HasPtr(const HasPtr &p):
ps(new std::string(*(p.ps))), i(p.i) {}
/*
*most copy assignment constructors combine the function
*of destructor and copy constructor.
*
*for instance, copy assignment constructor
*destroys the left side object's resource as destructor,
*and copies data from the right side object
*to the left one like copy constructor
*
*if possible, copy assignment constructor should be exception safe.
*which means when an exception occurs,
*it can put the left side object in a meaningful state
*/
HasPtr &operator=(const HasPtr &);
~HasPtr() {
delete ps;
}
private:
std::string *ps;
int i;
};
/*
*in this case, to ensure exception safety,
*we copy the right object's data first
*to ensure exception safety before assignment.
*then we delete the resource of the left object and execute assignment
*/
HasPtr &HasPtr::operator=(const HasPtr &rhs) {
auto newp = new std::string(*rhs.ps);
delete ps;
ps = newp;
i = rhs.i;
return *this;
}
classes which behave like a pointer
use reference count to control the underlying data
class HasPtr {
public:
HasPtr(const std::string &s = std::string()):
ps(new std::string(s)), i(0), use(new std::size_t(1)) {}
//copy the pointer to ensure they point to the same underlying data
//increase the reference count
HasPtr(const HasPtr &p):
ps(p.ps), i(p.i), use(p.use) {
++*use;
}
HasPtr &operator=(const HasPtr &);
~HasPtr();
private:
std::string *ps;
int i;
std::size_t *use; //reference count
};
HasPtr::~HasPtr() {
if (--*use == 0) {
delete ps;
delete use;
}
}
HasPtr &HasPtr::operator=(const HasPtr &rhs) {
++*rhs.use;
//execute the left object's destructor
if (--*use == 0) {
delete ps;
delete use;
}
//execute assignment
ps = rhs.ps;
i = rhs.i;
use = rhs.use;
return *this;
}
swap
we can swap the pointer to decrease extra memory allocation
class HasPtr {
friend void swap(HasPtr &, HasPtr &);
}
/*
*if a menber of a class has its own swap function,
*use the std::swap is an error
*and if we implicitly use swap function, the compiler use the overloaded one by default
*/
inline void swap(HasPtr &lhs, HasPtr &rhs){
using std::swap;
swap(lhs.ps,rhs.ps);
swap(lhs.i, rhs.i);
}
int main(){
HasPtr h1(),h2();
using std::swap;
swap(h1,h2); //use the overloaded one
}
//tip:this function can't overload the HasPtr &HasPtr::operator=(HasPtr &rhs)
HasPtr &HasPtr::operator=(HasPtr rhs){ //parameter is a copy, not a reference
swap(*this, rhs);
return *this; //rhs is destroyed
};
move constructor
library container, string and shared_ptr class support both move and copy operation
IO class and unique_ptr class can be moved, but can’t be copied
rvalue reference must be bound to an rvalue. we get rvalue reference through && instead of &
rvalue reference must be bound to an object ready to be destroyed
therefore, we can freely move an rvalue reference resource to another object
moreover, we can bound a const lvalue reference to an rvalue, as well
expression returns an rvalue. But rvalue reference variate itself is a lvalue
eg:
int i = 42; int &r = i; const int &r2 = i * 42; int &&r3 = i * 42; int &&r4 = r3; //illegal
all of them is legal except the last one
std::move avails to use a lvalue as an rvalue. What’s more, we must recognize that executing move means we promote not to use the original element unless we assign or destruct it.
int &&r4 = std::move(r3); //legal
//use std::move instead of move in order to prevent from potential naming conflict
an example of move constructor
StrVec::StrVec(StrVec &&s) noexcept //move operation shouldn't throw error
//initializer take over resource of s
: elements(s.elements), first_feet(s.first_feet), cap(s.cap) {
s.elements = s.first_feet = s.cap = nullptr;
//brilliant!
//get the spark of controlling dynamic memory!
//now destructing s is safe
//i wander weather we permit s to be meaningfully assigned,
//and after insperation, i find that void StrVec::push_back(const std::string &)
//has considered this situation. Now both assignment and destruction is ligal.
//and at the same time we can't read its value
//(certainly i put the whole code into my cpp file to avoid making my notes bloated)
}
move assignment constructor
StrVec &StrVec::operator=(StrVec &&rhs) noexcept {
//check self-assignment
//I guess this operation is designed to avoid freeing itself
//otherwise it may move meaningless data and put itself in a meaningless state
if (this != &rhs) {
free(); //release itself
elements = rhs.elements;
first_feet = rhs.first_feet;
cap = rhs.cap;
//put rhs in a destructible state
rhs.elements = rhs.first_feet = rhs.cap = nullptr;
}
return *this;
}
//copy and move operation's overload
StrVec v1, v2;
v1 = v2;
//copy assignment constructor
StrVec getVec(istream &); //getVec returns an rvalue
//because getVec() dosen't return value instead of reference
//up to now getVec() is just a declaration
v2 = getVec(cin);
//move assignment constructor
/*
*if we didn't define a move constructor
*the compiler will convert an rvalue reference to a const reference
*and call copy constructor
*/
reference qualifier
C++ permit us to assign value to a rvalue object(so weird)
eg:
string s1("a"), s2(3, 'b');
//s1 = "a", s2 = "bbb";
/*
*(s1 + s2) is an rvalue which never affects s1 and s2's value without assignment
*usually it is created by compiler and released soon
*the following expression execute:
* create a string temp_s = s1 + s2 = "abbb";
* assign "Wow" to temp_s and return "Wow";
* assign "Wow" to s3;
*
release temp_s
*Just from my own guess.If there's something wrong, I'll correct it
*/
string s3 = (s1 + s2) = "Wow";
cout << s3 << endl;
return 0;
we can use the reference qualifier to enforce the left object is an lvalue or an rvalue
class Foo {
public:
//the & in the back of parameter list enforce the left object is an lvalue
Foo &operator=(const Foo&) &;
//
};
Foo &Foo::operator=(const Foo& rhs) &{
//
return *this;
}
class Foo {
public:
Foo someMember() const &; //const should be in front of reference qualifier
}
reference qualifier and overloaded
class Foo{
public:
Foo sorted() &&;
Foo sorted() const &;
private:
vector<int> data;
};
//because object is an rvalue, we can change itself without any affection
Foo Foo::sorted() &&{
sort(data.begin(), data.end());
return *this;
}
//object is an lvalue, thus we can't affect its value
Foo Foo::sorted() const &{
Foo temp(*this);
sort(temp.data.begin(), temp.data.end());
return temp;
}
捕获列表 ↩︎
参数列表 ↩︎
最后
以上就是彩色蜜粉为你收集整理的C++ Primer learning notes #1C++ Primer learning notes的全部内容,希望文章能够帮你解决C++ Primer learning notes #1C++ Primer learning notes所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复