Skip to content

Instantly share code, notes, and snippets.

@zsrkmyn
Last active August 29, 2015 14:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zsrkmyn/ae7e1c7ae165b3302dc0 to your computer and use it in GitHub Desktop.
Save zsrkmyn/ae7e1c7ae165b3302dc0 to your computer and use it in GitHub Desktop.
C++ 学习笔记
//<eff C++ 3rd edtion>, clause 3
class Text {
public:
Text() : _text(new char[100]), _length(100) {}
~Text() { delete [] _text; }
const char & operator[](int i) const
{
if (i < _length)
return _text[i];
else
// do something else
return _text[0];
}
char & operator[](int i)
{
// avoid duplicate
return const_cast<char &>
(static_cast<const Text &>(*this)[i]);
}
private:
int _length;
char *_text;
};
int main()
{
Text t;
const Text ct;
t[10];
ct[10];
}
#include <iostream>
#include <algorithm>
#include <cstring>
#include <memory>
class MyString {
public:
// Common constructor
MyString(const char * str) :
_len(strlen(str))
{
std::cout << "in common constructor, source: " << str << std::endl;
_str = new char[_len + 1];
strncpy(_str, str, _len);
_str[_len] = 0;
}
// Copy constructor
MyString(const MyString & str) noexcept :
_len(str._len)
{
std::cout << "in copy constructor, source: " << str._str << std::endl;
_str = new char[_len + 1];
strncpy(_str, str._str, _len);
_str[_len] = 0;
}
// Move constructor
MyString(MyString && str) noexcept :
_str(str._str),
_len(str._len)
{
std::cout << "in move constructor, source: " << str._str << std::endl;
str._len = 0;
str._str = nullptr;
}
// Copy assignment
MyString & operator=(const MyString & str) noexcept
{
std::cout << "in copy assignment, source: " << str._str << std::endl;
if (&str != this) {
_free();
_len = str._len;
_str = new char[_len];
strncpy(_str, str._str, _len);
_str[_len] = 0;
}
return *this;
}
// Move Assignment
MyString & operator=(MyString && str) noexcept
{
std::cout << "in move assignment, source: " << str._str << std::endl;
if (&str != this) {
_free();
_len = str._len;
_str = str._str;
/*
* Note:
* The following two lines are essential!
* If these two lines are missing, _str will
* be freed when deleting rhs, and accessing
* the new object's _str member will cause
* core dump.
*
* See:
* <C++ primer 5ed> 13.6.2
* A Moved-from Object Must Be Destructible
*/
str._str = nullptr;
str._len = 0;
}
return *this;
}
~MyString() { delete [] _str; }
private:
void _free() { delete [] _str; }
char * _str;
int _len;
};
MyString foo()
{
return MyString("Goodbye");
}
template<class T, class Arg>
std::shared_ptr<T> factory(Arg && arg)
{
/*
* Note:
* std::forward implements the 'perfect forwarding'.
* Take MyString for example, if Arg is a Lvalue,
* factory() will call MyString's copy constructor.
* if Arg is a Rvalue, factory() will call MyString's
* move constructor.
*
* See:
* http://jxq.me/2012/06/06/%E8%AF%91%E8%AF%A6%E8%A7%A3c%E5%8F%B3%E5%80%BC%E5%BC%95%E7%94%A8/#section_06
*/
return std::shared_ptr<T>(new T(std::forward<Arg>(arg)));
}
int main ()
{
MyString str1("Hello"); // common constructor
MyString str2("World"); // common constructor
MyString str3(str1); // copy constructor
MyString str4(std::move(str1)); // move constructor
MyString str5(foo()); // common constructor in foo() NOTE: this is RVO
str1 = str2; // copy assignment
MyString str6("kill me!"); // common constructor
str6 = foo(); // move assignment
std::shared_ptr<MyString> pstr1 = factory<MyString>(foo()); // factory() calls move constructor
std::shared_ptr<MyString> pstr2 = factory<MyString>(str1); // factory() calls copy constructor
return 0;
}
// Output:
// in common constructor, source: Hello
// in common constructor, source: World
// in copy constructor, source: Hello
// in move constructor, source: Hello
// in common constructor, source: Goodbye
// in copy assignment, source: World
// in common constructor, source: kill me!
// in common constructor, source: Goodbye // called by foo() in line 121
// in move assignment, source: Goodbye
// in common constructor, source: Goodbye // called by foo() in line 123
// in move constructor, source: Goodbye
// in copy constructor, source: World

shared_ptr 最大的问题是不能解决循环依赖问题,所以有时候必须使用 weak_ptr 来解决问题。比如构造双向链表的时候,如果前驱和后继都使用 shared_ptr,就会造成内存泄漏。

#include <iostream>
#include <memory>
 
using namespace std;
 
class node {
	public:
		node(int val):
			_next(nullptr),
			_data(val)
		{}
 
		~node() { cout << _data << ": I am deleted" << endl; }
 
		void setNext(shared_ptr<node> next) { _next = next; }
		void setPrior(shared_ptr<node> prior) { _prior = prior; }
		shared_ptr<node> getNext() { return _next; }
 
	private:
		node();
 
		shared_ptr<node> _next;
		weak_ptr<node> _prior;
		int _data;
};
 
int main() {
	auto n1 = make_shared<node>(node(1));
 
	{ // this colon makes `p` local
	shared_ptr<node> p = n1;
	for (int i = 2; i < 7; i++) {
		auto n = make_shared<node>(node(i));
		p->setNext(n);
		n->setPrior(p);
		p = n;
	}
	}
 
	shared_ptr<node> n2 = make_shared<node>(node(9));
	{
	shared_ptr<node> p = n2;
	int i = 0;
	do {
		auto n = make_shared<node>(node(i + 10));
		n->setPrior(p);
		p->setNext(n);
		p = n;
		i++;
	} while (i < 5);
	}
 
	cout << "------------" << endl;
 
	n1->getNext()->getNext()->getNext()->setNext(n2);
	cout << "------------" << endl;
	n2->setPrior(n1->getNext()->getNext()->getNext());
 
	cout << "------------" << endl;
}

输出

1: I am deleted
2: I am deleted
3: I am deleted
4: I am deleted
5: I am deleted
6: I am deleted
9: I am deleted
10: I am deleted
11: I am deleted
12: I am deleted
13: I am deleted
14: I am deleted
------------
5: I am deleted
6: I am deleted
------------
------------
1: I am deleted
2: I am deleted
3: I am deleted
4: I am deleted
9: I am deleted
10: I am deleted
11: I am deleted
12: I am deleted
13: I am deleted
14: I am deleted

其中前 16 和 914 是 make_shared 过程中产生的零时对象析构造成的。 如果把上面 weak_ptr<node> _prior 改为 shared_ptr<node> _prior,输出变为:

1: I am deleted
2: I am deleted
3: I am deleted
4: I am deleted
5: I am deleted
6: I am deleted
9: I am deleted
10: I am deleted
11: I am deleted
12: I am deleted
13: I am deleted
14: I am deleted
------------
------------
------------
//<eff C++ 3rd edtion>, clause 25
#include <iostream>
namespace MyNamespace {
template<class T>
class Widget {
public:
//...
void swap(Widget<T> & rhs)
{
std::cout << "in MyNamespace" << std::endl;
auto tmp = rhs._pImpl;
rhs._pImpl = _pImpl;
_pImpl = tmp;
}
//...
private:
//...
int *_pImpl;
//...
};
template<class T>
void swap(Widget<T> & lhs, Widget<T> & rhs)
{ lhs.swap(rhs); }
}
int main()
{
using std::swap;
MyNamespace::Widget<int> o1, o2;
swap(o1, o2);
// output:
// in MyNamespace
}
// Note:
// This example just suit for `template class with pImpl`.
// The reason why we implement swap() above is because fuctions
// in C++ doesn't not support partial specialization.
//
// For `class with pImpl`, just fully specialize std::swap.
// For `class without pImpl`, just use std::swap.
//
// Four points to implement such a swap:
// 1. Implement a swap() as a member method of the template class;
// 2. Implement a swap() in the namespace where the class is;
// 3. Before calling swap(), use `using std::using` instead of
// `std::swap()`. The former style will allow C++ to choose the
// best swap() method for the object;
// 4. No exception is allowed in user-defined swap().
//
// See <eff C++ 3rd edtion>, clause 25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment