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开辟的堆空间。

 /*
* File: string.cc
* Time: 2018-12-18 19:36:01
* Author: wyzhang
*/ #include <cstdio>
#include <cstring>
#include <iostream> using std::cout;
using std::endl; class String {
public:
String()
: _ptr(nullptr) {
_ptr = nullptr;
cout << __FUNCTION__ << endl;
}
String(const char * s)
: _ptr(new char[strlen(s) + ]()) {
cout << "String(const char * s)" << endl;
strcpy(_ptr, s);
} String & operator = (const char * s) {
cout << "String & operator = (const char * s)" << endl;
_ptr = new char[strlen(s) + ]();
strcpy(_ptr, s);
} String & operator = (const String & rhs) {
cout << "String & operator = (const String & rhs)" << endl;
if (&rhs == this) {
return *this;
}
_ptr = new char[strlen(rhs._ptr)+]();
strcpy(_ptr, rhs._ptr);
}
String(const String & rhs)
: _ptr(new char[strlen(rhs._ptr) + ]()) {
cout << "String(const String & rhs)" << endl;
strcpy(_ptr, rhs._ptr);
}
~String() {
cout << "~String()" << endl;
if (_ptr) {
delete[] _ptr;
}
}
void print() {
if (!_ptr) {
cout << "_ptr is nullptr. " << endl;
return;
}
cout << "string = " << _ptr << endl;
}
private:
char * _ptr;
};
int main () {
String str1;
str1.print(); String str2 = "hello world";
String str3("test"); cout << __LINE__ << ": " ; str2.print();
cout << __LINE__ << ": " ; str3.print(); String str4 = str3;
cout << __LINE__ << ": " ; str4.print(); str4 = str2;
cout << __LINE__ << ": " ; str4.print(); return ;
}

string.cc

5.单例模式复习

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

 /*
* File: singleton.cc
* Time: 2018-12-19 10:22:18
* Author: wyzhang
*/ //要求: 在内存中只能创建一个对象
//1. 该对象不能是栈(全局)对象
//2. 该对象只能放在堆中 //应用场景:
//1. 直接替换任意的全局对象(变量)//因为全局对象越少越好
//2. 配置文件
//3. 词典类 //实现步骤:
//1. 将构造函数私有化
//2. 在类中定一个静态的指针对象(一般设置为私有),并且在类外初始化为空
//3. 定义一个返回值为类指针的静态成员函数,
// 如果2中的指针对象为空,则初始化对象,以后在有对象调用该静态成员函数的时候,不再初始化对象,
// 而是直接返回对象,保证类在内存中只有一个对象 #include <cstdio>
#include <iostream> using std::cout;
using std::endl; class Singleton {
public:
static Singleton * getInstance() { //不是静态的成员函数就没法调用,因为没有对象,需要类名调用
if (!_pInstance) {
_pInstance = new Singleton();
}
return _pInstance;
}
static void destory() {
if (_pInstance) {
delete _pInstance;
}
}
void print() {
cout << "Singleton::print() " ;
if (_pInstance) {
printf("_pInstance = %p \n", _pInstance);
}
}
private:
Singleton() {
cout << "Singleton()" << endl;
}
~Singleton() {
cout << "~Singleton(): " ;
printf("_pInstance = %p \n", _pInstance);
}
static Singleton * _pInstance;
}; Singleton * Singleton::_pInstance = ; /*
wyzhang@IdeaPad:~/code/c++/20181214/homework$ ./singleton
Singleton()
p1 = 0x555eef444e70
p2 = 0x555eef444e70
Singleton::print() _pInstance = 0x555eef444e70
~Singleton(): _pInstance = 0x555eef444e70
Singleton::print() _pInstance = 0x555eef444e70
~Singleton(): _pInstance = 0x555eef444e70
*/ int main () {
Singleton * p1 = Singleton::getInstance();
Singleton * p2 = Singleton::getInstance();
printf("p1 = %p\n", p1);
printf("p2 = %p\n", p2); p1->print();
p1->destory(); //可能写的有bug?能调用析构函数两次?
p2->print();
p2->destory(); return ;
}

singleton.cc

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

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

> 元素入栈 void push(int);

> 元素出栈 void pop();

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

> 判断栈空 bool emty();

> 判断栈满 bool full();

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

> 创建栈

> 将10入栈

> 将12入栈

> 将14入栈

> 读出并输出栈顶元素

> 出栈

> 读出并输出栈顶元素

 /*
* File: stack.cc
* Time: 2018-12-19 11:14:15
* Author: wyzhang
*/ #include <cstdio>
#include <iostream>
#include <vector> using std::cout;
using std::endl;
using std::vector; class Stack {
public:
Stack() { }
Stack(int n) {
nums.reserve(n); //capacity
}
~Stack() {
nums.clear();
}
void push(int num) {
if (full()) {
cout << "can not push, stk is full. input is " << num << endl;
return;
}
nums.push_back(num);
}
void pop() {
if (empty()) {
cout << "can not pop, stk is empty." << endl;
return;
}
nums.pop_back();
}
int top() {
if (empty()) {
cout << "can not get top, stk is empty." << endl;
return -;
}
return nums.back();
}
bool empty() {
return nums.size() == ;
}
bool full() {
return nums.size() == nums.capacity();
}
private:
vector<int> nums;
}; int main () {
Stack stk = Stack();
stk.push();
stk.push();
stk.push(); cout << "top of stk is " << stk.top() << endl;
stk.pop();
cout << "top of stk is " << stk.top() << endl; cout << "stk is emtpy : " << stk.empty() << endl;
cout << "stk is full : " << stk.full() << endl;
return ;
}

stack.cc

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

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

> 元素入队 void push(int);

> 元素出队 void pop();

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

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

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

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

 /*
* File: queue.cc
* Time: 2018-12-19 11:45:10
* Author: wyzhang
*/ #include <cstdio>
#include <iostream>
#include <vector> using std::cout;
using std::endl;
using std::vector; class Queue {
public:
Queue()
: first()
, rear()
, size()
, capacity() {
}
Queue(int num)
: first()
, rear()
, size()
, capacity(num) {
nums.reserve(num);
}
~Queue() {
nums.clear();
first = rear = -;
size = capacity = ;
}
void push(int num) {
if (full()) {
cout << "can not push element, queue is full. input is " << num << endl;
return;
}
++size;
nums[rear] = num;
rear = (rear + ) % capacity;
}
void pop() {
if (empty()) {
cout << "can not pop, queue is empty." << endl;
return;
}
--size;
first = (first + ) % capacity;
}
int front() {
if (empty()) {
cout << "can not get front, queue is empty." << endl;
return -;
}
return nums[first];
}
int back() {
if (empty()) {
cout << "can not get back, queue is empty." << endl;
return -;
}
int tmp_rear = ((rear + capacity) - ) % capacity;
return nums[tmp_rear];
}
bool empty() {
return size == ;
}
bool full() {
return size == capacity;
} private:
vector<int> nums;
int first, rear, size, capacity; void print() {
printf("capacity = %d, size = %d, first = %d, rear = %d, [", capacity, size, first, rear);
for (int i = ; i < capacity; ++i) {
printf(" %d", nums[i]);
}
printf("]\n");
}
}; int main () {
Queue que = Queue();
que.push();
printf("%d: que.front = %d, que.back = %d\n", __LINE__, que.front(), que.back());
que.push();
que.push();
que.push();
printf("%d: que.front = %d, que.back = %d\n", __LINE__, que.front(), que.back());
que.pop();
printf("%d: que.front = %d, que.back = %d\n", __LINE__, que.front(), que.back()); printf("begin while loop\n");
while (!que.empty()) {
printf("%d: que.front = %d, que.back = %d\n", __LINE__, que.front(), que.back());
que.pop();
}
return ;
}

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:
//......
};

最新文章

  1. 将Centos的yum源更换为国内的阿里云源
  2. text-transform属性
  3. 什么是F#
  4. SpringRMI解析2-RmiServiceExporter逻辑脉络
  5. JAVA线程同步辅助类Exchanger-交换
  6. Linxu 安装Scala
  7. xtrabackup工具安装
  8. Week1 Java 基础知识
  9. 阿里云centos增加swap(虚拟内存)
  10. POJ C++程序设计 编程题#1 编程作业—继承与派生
  11. HDU2102 A计划
  12. trackr: An AngularJS app with a Java 8 backend – Part IV 实践篇
  13. 在VS.NET中根据条件设置不同的MainForm
  14. jQuery Ajax 实例 ($.ajax、$.post、$.get)转
  15. [ACM] hdu 4405 Aeroplane chess (概率DP)
  16. J2SE-反射
  17. cnblogs
  18. MySQL常用的锁机制 ----------顾名思义
  19. 阶段01Java基础day25网络编程
  20. Python—模块介绍

热门文章

  1. Linux --忘记root密码/su: Authentication failure
  2. AGC020C Median Sum
  3. java资料搜索网站
  4. Linux系统下安装JDK及环境配置
  5. 浅析弹性公网IP付费模式和短时升配功能介绍
  6. mybatis的Date类型。
  7. post方式请求数据
  8. 【VisualStdio】在VS2015中显示上下文菜单中“创建单元测试”菜单
  9. 自动化测试常用断言的使用方法(python)-(转载@zhuquan0814
  10. java多线程学习笔记(七)