本博客仅用来记录自己学习本课程的笔记。
课程视频: 快速学习C和C++,基础语法和优化策略,学了不再怕指针(南科大计算机系原版)_哔哩哔哩_bilibili
1、parameter表示形参,argument表示实参
2、C++中class,如果不进行编写,默认的函数
1)默认构造函数,函数内没有任何内容
2)默认析构函数,函数没有任何内容
3)默认拷贝构造函数,函数会将非静态值复制给目标(注意与4进行区分)
4)默认拷贝赋值运算符,函数会将非静态值复制给目标(注意与3进行区分)
3、默认函数在使用时会出现的问题:
#pragma once
#include <iostream>
#include <cstring>
class MyString
{
private:
int buf_len;
char * characters;
public:
MyString(int buf_len = 64, const char * data = NULL)
{
std::cout << "Constructor(int, char*)" << std::endl;
this->buf_len = 0;
this->characters = NULL;
create(buf_len, data);
}
~MyString()
{
delete []this->characters;
}
bool create(int buf_len, const char * data)
{
this->buf_len = buf_len;
if( this->buf_len != 0)
{
this->characters = new char[this->buf_len]{};
if(data)
strncpy(this->characters, data, this->buf_len);
}
return true;
}
friend std::ostream & operator<<(std::ostream & os, const MyString & ms)
{
os << "buf_len = " << ms.buf_len;
os << ", characters = " << static_cast<void*>(ms.characters);
os << " [" << ms.characters << "]";
return os;
}
};
#include <iostream>
#include "mystring.hpp"
using namespace std;
// Why memory leak and memory double free?
int main()
{
{
MyString str1(10, "Shenzhen");
cout << "str1: " << str1 << endl;
MyString str2 = str1;
cout << "str2: " << str2 << endl;
MyString str3;
cout << "str3: " << str3 << endl;
str3 = str1;
cout << "str3: " << str3 << endl;
}
cout << "end of main()" << endl; // it will not be printed out
return 0;
}
如上图所示,三个MyString会调用三次构造函数,但是character同时指向一个地址,在最后调用析构函数时会调用三次删除character的内存,即重复删除内存。
解决办法:1)、hard copy,在构造函数和拷贝构造函数中进行重新申请一块内存
2)、soft copy,使用refcount(reference count)
4、smart pointers(原理就是一种模板类)
1)shared_ptr不同类的对象指向的同一地址,最后可以自动调用析构函数
2)unique_str一个地址只能由一个指针指向,最后可以自动调用析构函数,在转移对象时使用move函数
5、访问控制
关于友元类见http://t.csdnimg.cn/PSwjK
6、Derived class继承类
#include<iostream>
using namespace std;
class Student {
protected:
const char* name;
int num;
public:
Student(const char* name, int num) :name(name), num(num) {};
void display(void);
};
void Student::display(void) {
cout << "name:" << name << "num:" << num << endl;
}
class Pri_Student:public Student {
private:
int height;
int weight;
public:
Pri_Student(const char* name, int num, int h, int w) :Student(name, num) { height = h; weight = w; };
//不仅需要对派生类增加的数据成员的初始化,也要考虑基类的数据成员的初始化(直接调用基类的初始化函数)
//格式:派生类的构造函数(全部成员参数):基类的构造函数(基类的成员参数){派生类参数的初始化}
//实现形式非常类似与参数初始化列表
//也可以用参数初始化列表一并进行参数初始化
//Pri_Student(char name[], int num, int h, int w) :Student(name, num) ,height(h),weight(w){};
void display1(void);
void display(void); //在子类中也可以定义与基类相同名字的函数,其定义会覆盖基类的函数定义
};
void Pri_Student::display(void) {
cout << "display" << endl;
}
void Pri_Student::display1(void) {
cout << "name: " << name << "num: " << num << "height: " << height << "weight: " << weight << endl;
}
int main(void) {
Pri_Student S1("Sam", 1006, 183, 72);
S1.display();
S1.display1();
return 0;
}
1)如果在派生类中出现内存的申请,应该重新编写a copy constructor 和 an assignment operator.
2)对于不同继承方式:
1)使用public,则父类的public与protected依旧是public与protected,private不能继承;
2)使用protected,则父类的public与protected都为protected访问控制,private不能继承;
3)使用private,则父类的public与protected都为private访问控制,private不能继承;
3)* 友元函数与派生类的区分: # 友元函数需要在基类中声明,可以直接访问基类的成员数据(私有成员也可以直接访问) # 派生类只能访问基类中的公用变量以及保护变量,不可以直接访问基类中的私有变量
7、虚函数,用来
-
继承和扩展:派生类可以继承基类的虚函数,并提供一个新的实现(重写)。这样可以保持接口的一致性,同时允许每个派生类根据自己的需要定制行为。
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
string name;
Person(string n): name(n){}
virtual void print()//void print()
{
cout << "Name: " << name << endl;
}
};
class Person2
{
public:
string name;
Person2(string n): name(n){}
virtual void print() = 0; // = 0表示纯虚函数,表示改类只能进行继承,不能进行声明
};
class Student: public Person
{
public:
string id;
Student(string n, string i): Person(n), id(i){}
void print()
{
cout << "Name: " << name;
cout << ". ID: " << id << endl;
}
};
void printObjectInfo(Person & p)
{
p.print();
}
int main()
{
{
Student stu("yu", "2019");
printObjectInfo(stu);
}
{
Person * p = new Student("xue", "2020");
p->print(); //if print() is not a virtual function, different output
delete p; //if its destructor is not virtual
}
{ //if you want to call a function in the base class
Student stu("li", "2021");
stu.Person::print();
Person * p = new Student("xue", "2020");
p->Person::print();
delete p;
}
return 0;
}
使用virtual function时输出结果为
Name: yu. ID: 2019
Name: xue. ID: 2020
Name: li
Name: xue
不使用virtual function时输出结果为
Name: yu
Name: xue
Name: li
Name: xue
8、类模板(同函数模板)
#include <iostream>
using namespace std;
// Class Template
template<typename T>
class Mat
{
size_t rows;
size_t cols;
T * data;
public:
Mat(size_t rows, size_t cols): rows(rows), cols(cols)
{
data = new T[rows * cols]{};
}
~Mat()
{
delete [] data;
}
Mat(const Mat&) = delete; //删除拷贝函数
Mat& operator=(const Mat&) = delete; //删除赋值拷贝
T getElement(size_t r, size_t c);
bool setElement(size_t r, size_t c, T value);
};
template <typename T>
T Mat<T>::getElement(size_t r, size_t c)
{
if ( r >= this->rows || c >= this->cols)
{
cerr << "getElement(): Indices are out of range" << endl;
return 0;
}
return data[ this->cols * r + c];
}
template <typename T>
bool Mat<T>::setElement(size_t r, size_t c, T value)
{
if ( r >= this->rows || c >= this->cols)
{
cerr << "setElement(): Indices are out of range" << endl;
return false;
}
data[ this->cols * r + c] = value;
return true;
}
template class Mat<int>; // Explicitly instantiate template Mat<int>
//template Mat<float> and Mat<double> will be instantiate implicitly
int main()
{
Mat<int> imat(3,4);
imat.setElement(1, 2, 256);
Mat<float> fmat(2,3);
fmat.setElement(1, 2, 3.14159f);
Mat<double> dmat(2,3);
dmat.setElement(1, 2, 2.718281828);
// Mat<float> fmat2(fmat); //error
// Mat<float> fmat3(2,3);
// fmat3 = fmat; //error
cout << imat.getElement(1,2) << endl;
cout << fmat.getElement(1,2) << endl;
cout << dmat.getElement(1,2) << endl;
return 0;
}
9、模板非类型参数
#include <iostream>
using namespace std;
// Class Template
template<typename T, size_t rows, size_t cols>
class Mat
{
T data[rows][cols];
public:
Mat(){}
// // the default copy constructor will copy each element of a static array member
// // so we do not 'delete' the copy constructor
// // the same with the assignment operator
// Mat(const Mat&) = delete;
// Mat& operator=(const Mat&) = delete;
T getElement(size_t r, size_t c);
bool setElement(size_t r, size_t c, T value);
};
template<typename T, size_t rows, size_t cols>
T Mat<T, rows, cols>::getElement(size_t r, size_t c)
{
if ( r >= rows || c >= cols)
{
cerr << "getElement(): indices are out of range" << endl;
return 0;
}
return data[r][c];
}
template<typename T, size_t rows, size_t cols>
bool Mat<T, rows, cols>::setElement(size_t r, size_t c, T value)
{
if ( r >= rows || c >= cols)
{
cerr << "setElement(): Indices are out of range" << endl;
return false;
}
data[r][c] = value;
return true;
}
template class Mat<int, 2, 2>; // Explicitly instantiate template Mat<int, 2, 2>
typedef Mat<int, 2, 2> Mat22i;
//template Mat<float, 3, 1> will be instantiate implicitly
int main()
{
Mat22i mat;
mat.setElement(2, 3, 256);
cout << mat.getElement(2, 3) << endl;
mat.setElement(1, 1, 256);
cout << mat.getElement(1, 1) << endl;
Mat<float, 3, 1> vec;
vec.setElement(2, 0, 3.14159f);
cout << vec.getElement(2, 0) << endl;
Mat<float, 3, 1> vec2(vec); //使用了拷贝构造函数,静态数组能够全部拷贝
cout << vec2.getElement(2, 0) << endl;
// vec2 = mat; //error
return 0;
}
10、类模板的特例化(类似于函数模板的特例化)
11、标准类:string、array、stack、vector、list、map、set......
12、输入输出流,用来编写日志将输出和error分开进行输出
13、assert
assert 是一个宏,用于在运行时(当程序正在执行时)检查特定条件,在调试程序时非常有用。要使用它,你需要在程序中包含头文件“assert.h”。
实例:
没有定义NDEBUG宏时,
#include <iostream>
#include <cassert>
int main(int argc, char ** argv)
{
assert( argc == 2);
std::cout << "This is an assert example." << std::endl;
return 0;
}
输出:
$ ./a.out
a.out: assert.cpp:6: int main(int, char**): Assertion `argc == 2' failed.
Aborted (core dumped)
定义了NDEBUG宏时,(注意要在cassert前进行定义)
#include <iostream>
#define NDEBUG
#include <cassert>
int main(int argc, char ** argv)
{
assert( argc == 2);
std::cout << "This is an assert example." << std::endl;
return 0;
}
输出:
$ ./a.out
This is an assert example.