Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CC #61

Open
magicdawn opened this issue Jan 23, 2016 · 11 comments
Open

CC #61

magicdawn opened this issue Jan 23, 2016 · 11 comments

Comments

@magicdawn
Copy link
Owner

CC = c plus plus

@magicdawn magicdawn added the Book label Jan 23, 2016
@magicdawn magicdawn changed the title Book: C primer plus v4-zh CC Feb 27, 2016
@magicdawn magicdawn removed the Book label Feb 27, 2016
@magicdawn
Copy link
Owner Author

reference

引用

引用变量是已定义的变量的一个别名.

引用变量的主要用途是用作函数的形参, 函数将使用原始数据, 而不是其副本.

int x = 10;
int& y = x; // int& 是类型, 即指向int类型的引用

x,y 所指向的值 / x和y的地址都相同, 即 x == y & &x == &y

引用与const指针类似

  • 引用在声明时必须初始化, int& y = x; int* const px = &x;
  • 引用一旦与某个变量关联起来, 就一直效忠于他. 类似 const 指针不能改变指向.

const 引用

  1. 首先引用是不能改变指向的.
  2. 加了const代表不能使用引用改变member, 例如 struct / class
  3. const引用作为形参时, 实参可以为表达式, 而不必是变量, 因为函数内部不会去更改它.

返回值为引用类型

需要注意: 应避免返回在函数终止时在内存单元中不存在的内存单元引用. 例如

const awesome_struct& awesome_fn(const awesome_struct& arg){
    awesome_struct tmp = arg;
    return arg
}

在awesome_fn返回时, tmp变量就被销毁了, 返回tmp作为引用就存在问题(什么问题...空引用?), 同样应避免返回指向临时变量的指针. 最简单的是使用传入参数arg来作为返回值.

_为何在返回值前加了 const, 加了const之后, 函数的返回值变成了不可修改的左值, 可以 awesome_fn(arg) = awesome_variable;, 会玩! _

@magicdawn
Copy link
Owner Author

namespace

好像没什么好说的...

using declaration

namespace awesome_ns{
    int variable = 10;
}

void fn(){
    using awesome_ns::variable;
    variable = 1
    ::variable = 10;
}

上面 using 是一个 using 声明, 将 awesome_ns 中的一个 variable 变成在这个scope可用, ::variable 表示全局 namespace 中的同名 variable

using 编译指令

using namespace awesome_ns;

int main(){
    int a = variable;
}

using编译指令, 将整个 namespace 中所有的名称都可用.

@magicdawn
Copy link
Owner Author

template / generic

template <T> T add(T a, T b){
    return a + b;
}

int a = 1, b = 2;
add(a, b); // 直接调用

有时 template 可换为 class

specialization & instatiation

specialization

special即特别化, 书中称 具体化, 例如上面的add, 对于某些类型来说(如awesome_type), 是不能直接加的, 那么怎样也使用上template呢:

假设有一个awesome_type, 它的加法我们只想将 data 字段相加

template <> awesome_type add<awesome_type>(awesome_type a, awesome_type b){
    awesome_type ret;
    ret.data = a.data + b.data;
    return ret;
}

有了上面的代码之后, 再使用加法则会使用这个具体化之后的形式, 具体化, 我们加了awesome_type类型吗:

awesome_type a = { .data = 1 }, b = { .data = 2 };
add(a,b); // 回去调用具体化加了awesome_fn类型之后的

cpp中函数可以重载, 函数的优先级为:

  1. 普通函数 add(awesome_type, awesome_type)
  2. 具体化之后的模板函数 template <> awesome_type add<awesome_type>(awesome_type, awesome_type)
  3. 普通模板函数 template <T> T add(T,T)

  1. 其实不怎么懂还有 "具体化的模板函数", 你想具体化, 直接把 template 关键字去掉不就完了么, 优先级不就最高了么~

instantiation

instance实例, 说的是代码里面写个 template <T> T add<T,T> 我们有的是个模板, 还没有实例化, 在

int a = 1, b = 2;
add(a, b);

add(a, b) 的时候, 会去实例化一个 int add(int, int), 然后这个是自动进行的, 就叫 implicit instantiation, 其实还可以显示实例化

方法一

// explicit instantiation
template int add(int, int);

int a = 1, b = 2;
add(a, b);

这里使用 template int add(int, int) 表明它是使用某一个template, 而不是template声明.

方法二

int a = 1, b = 2;
add<int>(a, b);

显示实例化


  1. 我想说方法一哪个码农是闲的蛋疼了吧特么不理解一大堆名词, 搞这么复杂...

@magicdawn
Copy link
Owner Author

string & old char* in c

@magicdawn
Copy link
Owner Author

class

  • 通常在头文件只声明, 在cc文件中给出实现
class Demo{

private:
    int data;

public:
    int getData() { return data; }
};

成员

  • 在内部定义的函数, 访问成员, 不用加 this, 所有在构造函数的时候, 为了防止写出 a(成员) = a(形参) 这样的代码, 通常member都会加上 m_ 前缀, 例如 m_a
  • 貌似在类定义外访问member也是, 直接访问...
  • 在外部实现函数内容时加类名和双冒号 Demo::getData

构造函数

  • 可访问性应为public
  • 同构造函数, 不用写返回类型
  • 名字 = 类名
class Person{
private:
    int m_age;
    std::string m_name;

public:
    int getAge(){ return m_age; }
    std::string getName{ return m_name; }

    // constructor
    Person(){ /* default */ }
    Person(int& age, std::string& name)
};

调用

  • Person p 隐式调用
  • Person p = Person() 显示调用
  • Person *pp = new Person 隐式调用
  • Person p(18, "zhangsan") 调用

析构函数

  • 可访问性应为public
  • 同构造函数, 不用写返回类型
  • 名字 = ~类名
Person::~Person(){
    std::cout << "Bye" << std::endl;
}

this指针

前面 age/name 其实都是 this->age / this->name 简写, 每个成员函数都可以使用 this 指针来访问当前对象

函数最后的const

百度知道: http://zhidao.baidu.com/link?url=eRL-diqCNq9Nku4k454CrP_IR1Yel6QV8L39UuvvLUcing4UAnPJlcgiRwPIeJWgEopPtGwWOvMt_0j2P9nWFa

class Person{
public:
    int getAge() const { return this->age; }
};
  • 函数最后的const, 表示传入的 this 指针是指向const的指针, 表示不回通过 this 去修改这个对象
  • 只有成员函数可以加这个 const, 其他的函数会报错
  • 一个const对象, 只能调用 const 成员函数, 不能调用非const成员函数.

const方法表示不会干坏事:

  • 不管是谁, 调用const方法, 不会发生坏事
  • const对象调用非const方法, 可能会干坏事, 这个编译器是要组织的~
    orz...

enum class

c中enum不用限定符, 可能经常发生冲突

enum Size { small, large, xlarge };
enum ShipSize { large, extraLarge };

两个enum中有重复的, 编译不通过, 可以使用 enum class 解决

enum class Size { small, large, xlarge };
enum class ShipSize { large, extraLarge };

// 使用class机制引用
Size::large
ShipSize::large

@magicdawn
Copy link
Owner Author

上面的是C++ primier第10章的clas内容, clas内容较多, 分章节写笔记

class in ch11

运算符重载

函数名 operator+ 重载 + 运算符

拿个例子来说, 让Person类重载 +, 实现如下效果

Person::Person(int age){
    this->age = age;
}

Person Person::operator+ (int age){
    this->age += age;
    return *this;
}

Person p = Person(18);
p = p + 1; // 加上 int, 自动增加年龄

友元

  • 友元函数
  • 友元类
  • 友元成员函数

让函数成为类的友元, 可以赋予该函数与类的成员函数有相同的访问权限.

上述重载例子, 是 p + 1, 于是找到了 p.operator+(1), 但是碰到 1 + p的情形还是没办法处理, 可以使用友元函数:

#include <iostream>

using namespace std;

class Person{

public:
  // member
  int age;

  // constructor
  Person(int age){ this->age = age; }

  // operator+
  Person operator+(int age) { 
    this->age += age; 
    return *this; 
  }

  // 1 + p
  friend Person operator+(int age, Person& p);
};

// 友元
Person operator+(int age, Person& p){
  p.age += age;
  return p;
}

int main(){
  Person p(18);
  p = 1 + p;
  cout << p.age << endl;
  return 0;
}
  • 使用 friend 定义, 在实现的时候, 不带类信息, 不带friend, 将 operator+ 作为方法名的普通方法
  • 重载运算符时第一个参数默认不传, 为this. 友元函数重载时两个都要传

常用的友元, 重载 << 运算符

class T{
};
T t;

cout << t << end;;
  • cout 是 ostream 对象
  • tT 实例

若要在 class T 中实现重载这个运算符, 会是这样

class T{
public:
    ostream operator<<(ostream cout){
        cout << this->name << endl;
    }
};

然后只能这样用 t<<cout = t.operator<<(cout), 但这不是我们想要的, 需要将 T 作为第二个操作数重载, 于是在 class T 中使用友元函数.

class T{
private:
    char* name;
public:
    friend ostream& operator<<(ostream& cout, T& t);
};


ostream& operator<<(ostream& cout, T& t){
    cout << "t.name = " << t.name << endl;
    return cout;
}

类型转换

class T{
private:
    double x;
public:
    T(double x){ this->x = x; }
};

这里有一个单参数的构造函数, 于是可以写出

double x = 1.0;
T t = x;

这样会创建一个T实例, 然后赋值给 t, 采用逐属性赋值方法赋值.
可以使用 explicit T(double x){ this->x = x; } 一个 explicit 参数来表示禁止隐式类型转换.

上面说的都是 -> 类类型 转换

可以隐式转换

类类型 -> 显示转换

class T{
private:
    double x;
public:
    explicit T(double x){ this->x = x; }
    operator double() const { return this->x; }
};

T t(10.0);
double x = double(t);
double x = (double)t;

定义了一个 operator double() 成员函数, 可以这样强制转换为 double 类型

在类外定义为:

T::operator double() const{
    return this->x;
}
  • 没有返回类型
  • T::operator double() 函数名比较奇怪

  • 12章 类, 动态内存
  • 13章 类继承
  • 14章 代码重用
  • 15章 友元异常和其他
  • 16章 string类 & STL

好多内容啊 😂 😹 先歇着了...

@magicdawn
Copy link
Owner Author

magicdawn commented Mar 8, 2016

ch12 类和动态内存分配

  • int* pi = new int[5]; delete[] pi;
  • int* px = new int(3); delete pi;

复制构造函数

class T{
private:
    int num;
public:
    T(int num){ this->num = num; }
};

T b = T(1);
T b(a); // form1
T b = T(a); // form2
T b = a; // form3
T* pb = new T(a); // form4

这四种情况下, 同 a 去初始化 b, 就需要 复制构造函数 了.

class T{
public:
    T(const T& a);
};

调用 T(const T& b) 去构造 a

赋值运算符重载 operator=

class ClassName{
public:
    // 重载 `=`
    const ClassName& operator=(const ClassName& ref) {
        // blabla
    }
};
ClassName a = ClassName(b); // 调用复制构造函数
a = c;  // 调用 `operator=`

析构函数

第五版 381

  1. 调用函数, 按值传递的时候, 实参会在函数结束, 自动调用析构函数. 如果实例中有指针成员, 按值传递, 会导致析构函数将指针指向内存销毁. 其实也是调用复制构造函数, 赋值指针成员指向
  2. 复制构造函数,即上面例子中, 默认的复制构造函数, 会复制简单类型, 指针成员会公用, 即指向相同.
#include <iostream>

using namespace std;

class T {
public:
  int *ptr;

  T(int val) {
    this->ptr = new int;
    *this->ptr = val;
  }

  // 复制构造函数
  T(T &t) {
    cout << "copy constructor ..." << endl;
    // return T(*t.ptr);
    this->ptr = t.ptr;
  }

  ~T() {
    cout << "removing T" << endl;
    delete this->ptr;
  }
};

void callByValue(T);
void callByreference(T &);

int main(int argc, char *agrv[]) {
  T t(10);
  callByValue(t);
  callByreference(t);
  return 0;
}

void callByValue(T t) { cout << "by value " << *t.ptr << endl; }

void callByreference(T &t) { cout << "by reference " << *t.ptr << endl; }

在调用完 callByValue 之后, 形参 t 会调用析构函数, 最后出 main 函数的时候还会调用析构函数, t 还会调用析构函数, 但是 ptr 已经释放了...会引发错误...

update 2020-03-07

https://stackoverflow.com/questions/25968902/destructor-called-when-objects-are-passed-by-value

因为没有提供 copy constructor, 于是指针成员指向相同

@magicdawn
Copy link
Owner Author

ch13

@magicdawn
Copy link
Owner Author

ch14

@magicdawn
Copy link
Owner Author

ch15

@magicdawn
Copy link
Owner Author

ch16

string

constructor

constructor explanation
string() 空字符串
string(const char* s) 使用 char*
string(const char* s, size_t n) n个字符
string(size_t n, char c) 使用 c 填充, length 为 n
string(const string& str, size_t pos) pos 到结尾, or 从 pos 开始的前 n 个元素
template<class Iter> string(Iter begin, Iter end) 即是 slice(begin, end), 包含begin, 不包含 end

运算符重载

string重载了 == != > 等运算符

find

size_t find(const string& str, size_t pos=0) 找一个子字符串的 index

size() / length()

都是返回 length

char* 交互

char* string.c_str()

c_str() 返回的是一个 char* 指针

auto_ptr

auto_ptr 只试用于 new & delete, 并不适用于 new [] & delete []

STL

vector

vector<T> t(size_t n);

可以使用 swap begin end sort 等操作vector实例

迭代器

vertot<T>::iterator pbegin = t.begin();
vertot<T>::iterator pend = t.end();

for(vector<T>::iterator pt = t.begin(); pt != t.end(); pt++) {
    cout << (*pt) << endl;
}

迭代器就像指针, 可以进行

  1. 解引用 *pbegin 指向t的第一个元素, *pend 指向最后一个元素的后面一个位置
  2. 可以进行 +- 操作
method explanation
push_back / push_front push / unshift
begin / end 获取头尾 iterator
erase(start_iterator, end_iterator) 抹除, 包含 start, 不包含 end
insert(start_iterator, 要被插入的iterator开始, 要被插入的iterator结束) ...
sort(start, end, compare_fn) 排序

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant