• 【C/C++】知识点系统复习 (第一周)


    2018/12/18 周二

    1. C++内存布局分为几个区域,每个区域有什么特点?

    主要可以分为 5 个区域,

    (1) 栈区:由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

    (2) 堆区:由程序员分配释放。

    (3) 全局/静态区:全局变量和静态变量的存储是放在一块的,在程序编译时分配。

    (4) 文字常量区:存放常量字符串

    (5) 程序代码区:存放函数体(类的成员函数,全局函数)的二进制代码

    内存布局见CSAPP第七章的图7-13,Linux运行时存储器映像

    衍生问题:

    1.1 栈和堆的区别

    (1) 管理方式不同;(栈是系统自动分配, 堆是需要程序员申请,程序员释放,使用不好可能 memory leak)

    (2) 空间大小不同;

    栈是向低地址扩展的数据结构,它的容量是系统预先设定好的,一般是1M或者2M?申请的时候不要超过栈的剩余空间。

    堆是向高地址扩展的数据结构,链表的方式来存储空闲内存地址的,不连续,链表遍历方向是从低地址向高地址。堆可以获得的空间受限于计算机系统中有效的虚拟内存的大小。(一般来说可以到达 4GB?)

    (3) 能否产生碎片不同;(堆是链表分配内存,容易产生碎片,栈不会产生碎片)

    对于堆来说,频繁的 new/delete操作势必会造成内存空间的不连续,从而造成大量的碎片,使得程序效率降低。对于栈来说,不会出现这个问题,因为栈是先进先出的。在栈顶弹出之前,永远不可能有一个内存块从栈中间弹出。

    (4) 生长方向不同;(栈:高地址->低地址(生长方向向下), 堆: 低地址->高地址(生长方向向上))

    (5) 分配方式不同;

    内存有两种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配是用 malloc, calloc 函数进行分配,但是栈的动态分配和堆的动态分配是不同的,栈的动态分配由编译器进行释放,不需要手动。

    (6) 分配效率不同;(栈:系统分配,速度快,堆:new出来的内存,速度慢)

    2.当定义类的时候,编译器会自动为类自动生成那些函数?这些函数各自有什么特点?

    构造函数,析构函数 

    3.什么是浅拷贝,什么是深拷贝?

    浅拷贝是说拷贝指向空间的指针,拷贝出来的目标对象的指针和原对象的指针指向内存的同一块空间。(几个对象共用一块内存空间)

    深拷贝是说拷贝对象的具体内容,其内容的地址是程序员向系统申请分配的,拷贝结束后拷贝的内容完全一样,但是使用的内存空间地址不同。

    4.实现一个自定义的string类,保证main函数的正确执行。(main函数已经给出)

    题解:主要是实现深拷贝情况下的构造函数和析构函数。构造函数包含无参构造函数,有参构造函数,拷贝构造函数,和赋值构造函数。析构函数可以delete开辟的堆空间。

     1 /*
     2  * File:   string.cc
     3  * Time:   2018-12-18 19:36:01
     4  * Author: wyzhang
     5 */
     6 
     7 #include <cstdio>
     8 #include <cstring>
     9 #include <iostream>
    10 
    11 using std::cout;
    12 using std::endl;
    13 
    14 class String {
    15 public:
    16     String() 
    17     : _ptr(nullptr) {
    18         _ptr = nullptr;
    19         cout << __FUNCTION__ << endl;
    20     }
    21     String(const char * s) 
    22     : _ptr(new char[strlen(s) + 1]()) {
    23         cout << "String(const char * s)" << endl;
    24         strcpy(_ptr, s);
    25     }
    26 
    27     String & operator = (const char * s) {
    28         cout << "String & operator = (const char * s)" << endl;
    29         _ptr = new char[strlen(s) + 1]();
    30         strcpy(_ptr, s);
    31     }
    32 
    33     String & operator = (const String & rhs) {
    34         cout << "String & operator = (const String & rhs)" << endl;
    35         if (&rhs == this) {
    36             return *this;
    37         }
    38         _ptr = new char[strlen(rhs._ptr)+1]();
    39         strcpy(_ptr, rhs._ptr);
    40     }
    41     String(const String & rhs) 
    42     : _ptr(new char[strlen(rhs._ptr) + 1]()) {
    43         cout << "String(const String & rhs)" << endl;
    44         strcpy(_ptr, rhs._ptr);
    45     }
    46     ~String() {
    47         cout << "~String()"  << endl;
    48         if (_ptr) {
    49             delete[] _ptr;
    50         }
    51     }
    52     void print() {
    53        if (!_ptr) {
    54            cout << "_ptr is nullptr. " << endl;
    55            return;
    56        }
    57        cout << "string = " << _ptr << endl;
    58     }
    59 private:
    60     char * _ptr;
    61 };
    62 int main () {
    63     String str1;
    64     str1.print();
    65 
    66     String str2 = "hello world";
    67     String str3("test");
    68 
    69     cout << __LINE__ << ": " ; str2.print();
    70     cout << __LINE__ << ": " ; str3.print();
    71 
    72     String str4 = str3;
    73     cout << __LINE__ << ": " ; str4.print();
    74 
    75     str4 = str2;
    76     cout << __LINE__ << ": " ; str4.print();
    77 
    78     return 0;
    79 }
    string.cc

    5.单例模式复习

    题解:感觉 singleton 还是写的有点点问题。先放着吧。这个务必学会手写。orz。

     1 /*
     2  * File:   singleton.cc
     3  * Time:   2018-12-19 10:22:18
     4  * Author: wyzhang
     5 */
     6 
     7 //要求: 在内存中只能创建一个对象
     8 //1. 该对象不能是栈(全局)对象
     9 //2. 该对象只能放在堆中
    10 
    11 //应用场景:
    12 //1. 直接替换任意的全局对象(变量)//因为全局对象越少越好
    13 //2. 配置文件
    14 //3. 词典类
    15 
    16 //实现步骤:
    17 //1. 将构造函数私有化
    18 //2. 在类中定一个静态的指针对象(一般设置为私有),并且在类外初始化为空
    19 //3. 定义一个返回值为类指针的静态成员函数,
    20 //      如果2中的指针对象为空,则初始化对象,以后在有对象调用该静态成员函数的时候,不再初始化对象,
    21 //      而是直接返回对象,保证类在内存中只有一个对象
    22 
    23 
    24 #include <cstdio>
    25 #include <iostream>
    26 
    27 using std::cout;
    28 using std::endl;
    29 
    30 class Singleton {
    31 public:
    32     static Singleton * getInstance() {  //不是静态的成员函数就没法调用,因为没有对象,需要类名调用
    33         if (!_pInstance) {
    34             _pInstance = new Singleton();
    35         }
    36         return _pInstance;
    37     }
    38     static void destory() {
    39         if (_pInstance) {
    40             delete _pInstance;
    41         }
    42     }
    43     void print() {
    44         cout << "Singleton::print() " ;
    45         if (_pInstance) {
    46             printf("_pInstance = %p 
    ", _pInstance);
    47         }
    48     }
    49 private:
    50     Singleton() {
    51         cout << "Singleton()" << endl;
    52     }
    53     ~Singleton() {
    54         cout << "~Singleton(): " ;
    55         printf("_pInstance = %p 
    ", _pInstance);
    56     }
    57     static Singleton * _pInstance;
    58 };
    59 
    60 Singleton * Singleton::_pInstance = 0;
    61 
    62 /*
    63 wyzhang@IdeaPad:~/code/c++/20181214/homework$ ./singleton
    64 Singleton()
    65 p1 = 0x555eef444e70
    66 p2 = 0x555eef444e70
    67 Singleton::print() _pInstance = 0x555eef444e70 
    68 ~Singleton(): _pInstance = 0x555eef444e70 
    69 Singleton::print() _pInstance = 0x555eef444e70 
    70 ~Singleton(): _pInstance = 0x555eef444e70 
    71 */
    72 
    73 int main () {
    74     Singleton * p1 = Singleton::getInstance();
    75     Singleton * p2 = Singleton::getInstance();
    76     printf("p1 = %p
    ", p1);
    77     printf("p2 = %p
    ", p2);
    78 
    79     p1->print();
    80     p1->destory();
    81     
    82     //可能写的有bug?能调用析构函数两次?
    83     p2->print();
    84     p2->destory();
    85 
    86     return 0;
    87 }
    singleton.cc

    6.编写一个类,实现栈操作。

    编写一个类,实现简单的栈。栈中有以下操作:

    > 元素入栈 void push(int);

    > 元素出栈 void pop();

    > 读出栈顶元素 int top();

    > 判断栈空 bool emty();

    > 判断栈满 bool full();

    如果栈溢出,程序终止。栈的数据成员由存放10个整型数据的数组构成。先后做如下操作:

    > 创建栈

    > 将10入栈

    > 将12入栈

    > 将14入栈

    > 读出并输出栈顶元素

    > 出栈

    > 读出并输出栈顶元素

     1 /*
     2  * File:   stack.cc
     3  * Time:   2018-12-19 11:14:15
     4  * Author: wyzhang
     5 */
     6 
     7 #include <cstdio>
     8 #include <iostream>
     9 #include <vector>
    10 
    11 using std::cout;
    12 using std::endl;
    13 using std::vector;
    14 
    15 class Stack {
    16 public:
    17     Stack() {
    18 
    19     }
    20     Stack(int n) {
    21         nums.reserve(n); //capacity
    22     }
    23     ~Stack() {
    24         nums.clear();
    25     }
    26     void push(int num) {
    27         if (full()) {
    28             cout << "can not push, stk is full. input is " << num << endl; 
    29             return;
    30         }
    31         nums.push_back(num);
    32     }
    33     void pop() {
    34         if (empty()) {
    35             cout << "can not pop, stk is empty." << endl;
    36             return;
    37         }
    38         nums.pop_back();
    39     }
    40     int top() {
    41         if (empty()) {
    42             cout << "can not get top, stk is empty." << endl;
    43             return -1;
    44         }
    45         return nums.back();
    46     }
    47     bool empty() {
    48         return nums.size() == 0;
    49     }
    50     bool full() {
    51         return nums.size() == nums.capacity();
    52     }
    53 private:
    54     vector<int> nums;
    55 };
    56 
    57 int main () {
    58     Stack stk = Stack(2);
    59     stk.push(10);
    60     stk.push(12);
    61     stk.push(14);
    62     
    63     cout << "top of stk is " << stk.top() << endl;
    64     stk.pop();
    65     cout << "top of stk is " << stk.top() << endl;
    66 
    67     cout << "stk is emtpy : " << stk.empty() << endl;
    68     cout << "stk is full : " << stk.full() << endl;
    69     return 0;
    70 }
    stack.cc

    7.编写一个类,实现简单队列操作 

    编写一个类,实现简单的队列。队列中有以下操作:

    > 元素入队 void push(int);

    > 元素出队 void pop();

    > 读取队头元素 int front();

    > 读取队尾元素 int back();

    > 判断队列是否为空 bool emty();

    > 判断队列是否已满 bool full();

      1 /*
      2  * File:   queue.cc
      3  * Time:   2018-12-19 11:45:10
      4  * Author: wyzhang
      5 */
      6 
      7 #include <cstdio>
      8 #include <iostream>
      9 #include <vector>
     10 
     11 using std::cout;
     12 using std::endl;
     13 using std::vector;
     14 
     15 class Queue {
     16 public:
     17     Queue()
     18     : first(0)
     19     , rear(0)
     20     , size(0)
     21     , capacity(0) {
     22     }
     23     Queue(int num) 
     24     : first(0)
     25     , rear(0)
     26     , size(0)
     27     , capacity(num) {
     28         nums.reserve(num);
     29     }
     30     ~Queue() {
     31         nums.clear();
     32         first = rear = -1;
     33         size = capacity = 0;
     34     }
     35     void push(int num) {
     36         if (full()) {
     37             cout << "can not push element, queue is full. input is " << num << endl;
     38             return;
     39         }
     40         ++size;
     41         nums[rear] = num;
     42         rear = (rear + 1) % capacity;
     43     }
     44     void pop() {
     45         if (empty()) {
     46             cout << "can not pop, queue is empty." << endl;
     47             return;
     48         }
     49         --size;
     50         first = (first + 1) % capacity;
     51     }
     52     int front() {
     53         if (empty()) {
     54             cout << "can not get front, queue is empty." << endl;
     55             return -1;
     56         }
     57         return nums[first];
     58     }
     59     int back() {
     60         if (empty()) {
     61             cout << "can not get back, queue is empty." << endl;
     62             return -1;
     63         }
     64         int tmp_rear = ((rear + capacity) - 1) % capacity;
     65         return nums[tmp_rear];
     66     }
     67     bool empty() {
     68         return size == 0;
     69     }
     70     bool full() {
     71         return size == capacity;
     72     }
     73 
     74 private:
     75     vector<int> nums;
     76     int first, rear, size, capacity;
     77 
     78     void print() {
     79         printf("capacity = %d, size = %d, first = %d, rear = %d, [", capacity, size, first, rear);
     80         for (int i = 0; i < capacity; ++i) {
     81             printf(" %d", nums[i]);
     82         }
     83         printf("]
    ");
     84     }
     85 };
     86 
     87 int main () {
     88     Queue que = Queue(3);
     89     que.push(1);
     90     printf("%d: que.front = %d, que.back = %d
    ", __LINE__, que.front(), que.back());
     91     que.push(2);
     92     que.push(3);
     93     que.push(4);
     94     printf("%d: que.front = %d, que.back = %d
    ", __LINE__, que.front(), que.back());
     95     que.pop();
     96     printf("%d: que.front = %d, que.back = %d
    ", __LINE__, que.front(), que.back());
     97     
     98     printf("begin while loop
    ");
     99     while (!que.empty()) {
    100         printf("%d: que.front = %d, que.back = %d
    ", __LINE__, que.front(), que.back());
    101         que.pop();        
    102     }
    103     return 0;
    104 }
    queue.cc

    8.封装Linux下互斥锁和条件变量

    9. 实现只能生成栈对象的代码

    10. 实现只能生成堆对象的代码

    11. 统计一篇英文(The_Holy_Bible.txt)文章中出现的单词和词频

    输入:某篇文章的绝对路径
    输出:词典(词典中的内容为每一行都是一个“单词 词频”)

    词典的存储格式如下
    -----------------
    | a 66 |
    | abandon 77 |
    | public 88 |
    | ...... |
    |_________________|

    class Dictionary
    {
    public:
    //......
    void read(const std::string & filename);
    void store(const std::string & filename);
    private:
    //......
    };
  • 相关阅读:
    Day08_固化命令、grep、sed及awk命令
    Day07_网络管理、SSH、shell及元字符
    Day06_nginx及反向代理、共享存储nfs
    安装Apache所踩的的坑
    使用JS制作小游戏贪吃蛇
    清除浮动的几种方式
    纯CSS3图片反转
    在JAVASCRIPT中,为什么document.getElementById不可以再全局(函数外)使用?
    关于钉钉开发,心得
    javascript计算两个时间差
  • 原文地址:https://www.cnblogs.com/zhangwanying/p/10138282.html
Copyright © 2020-2023  润新知