目录
本周每日学习记录
进展
重点内容
练习用代码结构
appendix
作为一名非计算机专业的大龄工科生,我正踏上转向编程(特别是图形学领域)的旅程。在这个过程中,C++成为了我的主要工具。回顾过去一年,我时断时续地学习C++,却发现自己经常在重复相同的概念,没有明确的学习方向,常感到知识点零散且琐碎。
一次偶然的机会,我在B站上看到了Rock老师和学生的交流视频,被他负责任的态度所吸引。在与他简短的沟通后,我决定加入奇牛软件学院。通过学院的项目实践,我希望能够以更系统、实用的方式来掌握C++。同时,我也期待对计算机科学有更深入的理解,尤其是数据结构和算法。
我设定了一个明确的目标:希望在未来一年里,在技能和知识上实现显著的提升。我希望能够将过去零散的学习经历转化为对C++的全面而连贯的理解,为我明年的校园招聘打下坚实的基础。
#Day 01 Thu
2024-01-11 17:57:21: Total study time: 00:10:21
2024-01-11 22:57:21: Total study time: 03:50:21
#Day 02 Fri
2024-01-12 20:52:01: Total study time: 02:43:37
2024-01-12 22:17:46: Total study time: 01:15:15
#Day 03 Sat
2024-01-13 20:09:27: Total study time: 00:36:39
2024-01-13 21:27:45: Total study time: 01:09:28
2024-01-13 22:31:53: Total study time: 01:00:14
2024-01-13 23:58:51: Total study time: 01:26:50
#Day 04 Sun
2024-01-14 18:58:38: Total study time: 00:52:57
2024-01-14 22:11:02: Total study time: 03:00:53
2024-01-14 22:37:37: Total study time: 00:26:19
项目设计思想:问题描述
>> 接口设计
>> 接口实现
>> 成员变量设计
sstream
(std::std::stringstream
)
示例用法:
std::string Car::description() const {
std::stringstream ret;
ret << "car brand: " << brand_ << "(" << model_ << ")" << "\n CURRENT MILES: " << miles_
<< "\nPrice: " << getPrice()
<< "\nEngine: " << engine_.description();
for(int i=0; i<4; ++i){
ret << "\nTire " << "[" << i << "]" << tires_[0].description();
}
ret << "\n";
return ret.str();
}
面向对象编程三大特性封装继承多态:class
>>encapsulation
>> inheritance
>> polymorphism
构造函数constructor
,析构函数destructor
,拷贝构造函数copy constructor
以及拷贝赋值函数copy assignment operator
的调用时机和顺序。
this
指针
Static member
类内静态成员的初始化不可以在类内进行,且仅初始化一次。
静态成员函数以及const成员函数的用法。
vector
中的push_back()
方法是一个值拷贝, 这可能会导致设计的偏差。
解决办法,采用指针构造vector
#include
std::vector vec;
MyClass obj;
vec.push_back(&obj); // 添加指向obj的指针
vec[0]->modify(); // 修改vec中的第一个元素,实际上修改的是obj
建模常用手段:组合composition
和聚合aggregation
组合是一种强“拥有”关系。在组合中,部分的生命周期依赖于整体的生命周期。当整体被销毁时,其部分也应该被销毁。
组合通常是通过在包含类中创建另一个类的对象实现的。
例子:考虑一个Car
和Engine
的关系。汽车有一个引擎当汽车被销毁时,其引擎也不再存在。
class Engine {
// Engine的实现
};
class Car {
private:
Engine engine; // Car包含一个Engine
public:
// Car的方法
};
聚合是一种弱的“拥有”关系,其中一个类(称为“整体”)包含另一个类(称为“部分”)的对象,但两者的生命周期不一定相关联。换句话说,在聚合关系中,部分可以独立于整体存在。
聚合通常通过在包含类中包含另一个类的对象或者指针来实现。
例子:考虑一个Class
和Student
的关系。一个班级包含学生,但学生可以存在于班级之外。
class Student {
// Student的实现
};
class Class {
private:
std::vector students; // Class包含多个Student
public:
void addStudent(const Student& s) {
students.push_back(s);
}
// Class的其他方法
};
菱形继承问题diamond inheritance structure
>> 继承二异性问题ambiguous error
solution: 显式调用或采用虚继承,起始基类被称为虚基类
示例用法:
#include
#include
class Tel{
public:
Tel():number_("unknown"){}
protected:
std::string number_;
};
//virtual inheritance can be used to solve the diamond inheritance structure
//the base class named as virtual base class(Tel)
//solving the problem of ambiguous error
//class Fixedline: public Tel{
class Fixedline: virtual public Tel{
};
//class MobilePhone:public Tel{
class MobilePhone: virtual public Tel{
};
class Wireless: public Fixedline, public MobilePhone{
public:
void setNumber(const std::string& num){
//ambiguous error
//in diamond inheritance structure, there will be multi-members inherited,
// while no way to tell from which
//explicitly define the member, but too complicated
//what is the solution?
//virtual inheritance
this->number_ = num;
// this->Fixedline::number_ = num;
}
std::string getNumber(){
// return this->Fixedline::number_;
return this->number_;
}
};
int main() {
Wireless w;
w.setNumber("1000000");
std::cout << w.getNumber() << std::endl;
return 0;
}
类继承的几种模式:public
, protected
, private
位图算法Bitmap algorithm
(空间换速度)
开辟一个足够大小的char型数组初始化为0, 遍历数组存储相应的数据
首先按照8位一组的方式采样数据并访问相应的单元,在单元内采用位运算存储和判断是否存在
具体代码如下所示:
#include
#include
/*
* bit map algorithm
* space -> time
* 4 billion finding issue
* */
void init(char* data, int len){
//design following the real problem
//assuming only (int number % 3 == 0) are stored
unsigned int n = len*8; //total number of datas
for (unsigned int i = 0; i < n; ++i){
//find the number
if ((i % 3) == 0){
//access the pointer -> find the place -> set the value to 1
//using the bit binary operator << and |
//to be noticed here, the | operator cannot change the value, while |= should be used here
*(data + (i/8)) |= (1 << (i % 8 ));
// data[i / 8] |= (1 << (i % 8));
}
}
}
bool checkMap(int value, int len, char* dataMap){
if(((value/8) > len) || (value < 0))
return false;
int ind = value/8;
char temp = 1 << (value%8);
//using bit and operation to check if the value is activated in the map or not
return ((dataMap[ind] & temp)!=0);
//0000 0000
//0010 0000
}
int main() {
//assign one enough memory for Bitmap
//8000000001
//2147483647 >> signed int
//4294967295 >> unsigned int
unsigned int n = 4000000000;
// one more promising length < 8, eg: 0, 1, 2, 3, 4, 5, 6
int len = n / 8 + 1;
char *data = static_cast (malloc(len));
//initialize the data at value zero across the Bitmap
memset(data, 0, len);
//load the data set(only once)
init(data, len);
/*=================testing=================*/
while(true){
std::cout << "please enter the value for searching(-1 for quit): ";
unsigned int value;
std::cin >> value;
if(value == -1)
break;
if(checkMap(value, len, data)){
std::cout << "found~" << std::endl;
}else{
std::cout << "no such one value~" << std::endl;
}
}
//always there is one free(delete) operation following the malloc(new)
free(data);
return 0;
}
#结果展示
please enter the value for searching(-1 for quit): 1
no such one value~
please enter the value for searching(-1 for quit): 0
found~
please enter the value for searching(-1 for quit): 3
found~
please enter the value for searching(-1 for quit): 6
found~
please enter the value for searching(-1 for quit): 9
found~
please enter the value for searching(-1 for quit): 999999
found~
please enter the value for searching(-1 for quit):
潜在问题:以上代码无法处理数据越界的问题,上述代码在数据越界时会导致std::cin的失效,这在极限测试中被发现例如输入数字8000000001
,这会造成无限循环(int
),或者直接退出(unsigned int
)。
#8000000001
#2147483647 >> signed int
#4294967295 >> unsigned int
#signed int value
please enter the value for searching(-1 for quit): 8000000001
please enter the value for searching(-1 for quit): no such one value~
please enter the value for searching(-1 for quit): no such one value~
please enter the value for searching(-1 for quit): no such one value~
please enter the value for searching(-1 for quit): no such one value~
please enter the value for searching(-1 for quit): no such one value~
please enter the value for searching(-1 for quit): no such one value~
please enter the value for searching(-1 for quit): no such one value~
please enter the value for searching(-1 for quit): no such one value~
please enter the value for searching(-1 for quit): no such one value~
please enter the value for searching(-1 for quit): no such one value~
please enter the value for searching(-1 for quit): no such one value~
...
#unsigned int value
please enter the value for searching(-1 for quit): 8000000001
Process finished with exit code 0
解决方法:在循环末尾对cin进行判断,采用一个来自
库中的模板类numeric_limits
#include
//...other codes...
//this is for the failure on cin
if (std::cin.fail()) {
// clear the error
std::cin.clear();
// solve the error
std::cin.ignore(std::numeric_limits::max(), '\n');
continue;
}
问题解决(值得注意的是unsigned int 直接退出循环了,并没有给cin
反映的时间。所以unsigned 类型没有得到纠正)结果如下:
please enter the value for searching(-1 for quit): 8000000001
no such one value~
please enter the value for searching(-1 for quit): 8000000001
no such one value~
please enter the value for searching(-1 for quit): 8000000001
no such one value~
please enter the value for searching(-1 for quit): 8000001
found~
please enter the value for searching(-1 for quit):
项目数据的永久存储
>> 存储到文件或存储到数据库中
c++中的 流对象
输出至 文件
or 控制台
or 特殊数据类型如 stringstream
流对象的继承关系如下图所示:
ostream
输出(cout
), istream
输入(cin
)
ofstream
文件输出, ifstream
文件输入
ostringstream
字符串输出, istringstream
字符串输入
iostream
{fstream
, stringstream
} 通用输入输出流
std::fsteam
是可读可写的,但是在其使用过程中要注意其默认模式是直接覆盖文件, 使用专业的方法会有利于代码的维护。
写出二进制类型时要注意int
类型的强行转换
// of << age << "\n";
of.write((char*)(&age), sizeof(age));
读取二进制时除相应的运用成员方法以外,还应注意 \t
space
等无法跳过的问题。
//still there is problem for type translation, also the `\t` cannot be identified
//using read() member function to read one more to ignore this character.
char tmp;
ifs.read((char*)&tmp, sizeof(tmp));
// ifs >> age;
ifs.read((char*)&age, sizeof(age));
std::cout << age << std::endl;
按照制定格式来写出文本文件(使用
中的 stringstream
)
std::stringstream s;
s << "name: " << name << "\t age: " << age << std::endl;
of << s.str();
$ tree -L 2
.
├── project01
│ ├── Boy.cpp
│ ├── Boy.h
│ ├── cmake-build-debug
│ ├── CMakeLists.txt
│ ├── Girl.cpp
│ ├── Girl.h
│ ├── main.cpp
│ ├── Person.cpp
│ ├── Person.h
│ └── readme.md
├── project02
│ ├── Character.cpp
│ ├── Character.h
│ ├── cmake-build-debug
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── Toy.cpp
│ └── Toy.h
├── project03
│ ├── Car.cpp
│ ├── Car.h
│ ├── cmake-build-debug
│ ├── CMakeLists.txt
│ ├── Engine.cpp
│ ├── Engine.h
│ ├── main.cpp
│ ├── Tire.cpp
│ └── Tire.h
├── project04
│ ├── cmake-build-debug
│ ├── CMakeLists.txt
│ └── main.cpp-
├── project05
│ ├── cmake-build-debug
│ ├── CMakeLists.txt
│ └── main.cpp
├── project06
│ ├── cmake-build-debug
│ ├── CMakeLists.txt
│ └── main.cpp
├── project07
│ ├── Book.cpp
│ ├── Book.h
│ ├── cmake-build-debug
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── SellBook.cpp
│ └── SellBook.h
├── project08
│ ├── cmake-build-debug
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── ODU330.cpp
│ ├── ODU330.h
│ ├── ODU.cpp
│ └── ODU.h
└── project09
├── biwriting.cpp
├── cmake-build-debug
├── CMakeLists.txt
└── main.cpp
18 directories, 44 files
心得:
虽然有过一些学习cpp的经历,但是经过这几天的学习发现自己还是有很多的不足,很多的点扣的不够细致。 目前的知识点单纯凭借代码练习还是可以记住的。
我的计划是在现阶段一些简单的内容倍速快速过一遍,为之后的数据结构算法以及linux系统编程抢出一部分时间。(因为不太敢轻易跳过rock老师的课,毕竟在代码练习里面可能会穿插一些我不了解的知识点)
以后会尽量单日内完成学习总结,并做好文件管理和问题分类,避免特别长的笔记。
记录由一个shell脚本自动生成,实现计时, 提醒等功能,最终写入至一个log日志中。(借助chatgpt
实现)
#!/bin/bash
total_work_time=14400 # 4 hours in seconds
work_period=3600 # 1 hour in seconds
start_time=$(date +"%Y-%m-%d %H:%M:%S")
start_timestamp=$(date +"%s")
trap 'cleanup_and_exit' INT TERM QUIT
cleanup_and_exit() {
end_time=$(date +"%Y-%m-%d %H:%M:%S")
end_timestamp=$(date +"%s")
total_study_time=$((end_timestamp - start_timestamp))
formatted_total_study_time=$(date -u -d @${total_study_time} +"%H:%M:%S")
echo "${end_time}: Total study time: ${formatted_total_study_time}" >> ~/qiniu/study.log
exit
}
while [ $total_work_time -gt 0 ]
do
seconds_left=$total_work_time
hours_init=$((seconds_left/3600))
mins_init=$((seconds_left%3600/60))
seconds_init=$((seconds_left%60))
hours_remain=${hours_init}
mins_remain=${mins_init}
seconds_remain=${seconds_init}之
echo "Working time in total is ${hours_init}:${mins_init}:${seconds_init}"
echo "========================================"
# Work for 1 hour
while [ $seconds_left -gt $(($total_work_time - $work_period)) ]
do
echo "Keep working for ${hours_remain}:${mins_remain}:${seconds_remain} more......"
sleep 1s
seconds_left=$((seconds_left - 1))
hours_remain=$((seconds_left/3600))
mins_remain=$((seconds_left%3600/60))
seconds_remain=$((seconds_left%60))
clear
echo "Working time in total is ${hours_init}:${mins_init}:${seconds_init}"
echo "========================================"
done
mplayer -volume 20 ~/Music/rest.mp3 # Play the 'rest' music
# Prompt user to continue or quit
while true
do
read -p "Type 'continue' to start the next work period or 'q' to quit: " user_input
if [ "$user_input" == "continue" ]
then
break
elif [ "$user_input" == "q" ]
then
cleanup_and_exit
else
echo "Invalid input. Please type 'continue' to proceed or 'q' to quit."
fi
done
total_work_time=$((total_work_time - work_period))
done
mplayer -volume 20 ~/Music/done.mp3 # Play the 'done' music upon exit
cleanup_and_exit