我是靠谱客的博主 彩色蜜粉,最近开发中收集的这篇文章主要介绍C++ Primer learning notes #1C++ Primer learning notes,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

  • 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;
}

  1. 捕获列表 ↩︎

  2. 参数列表 ↩︎

最后

以上就是彩色蜜粉为你收集整理的C++ Primer learning notes #1C++ Primer learning notes的全部内容,希望文章能够帮你解决C++ Primer learning notes #1C++ Primer learning notes所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(50)

评论列表共有 0 条评论

立即
投稿
返回
顶部