#ifndef __TESTCLASS_NOPOINT__
#define __TESTCLASS_NOPOINT__
#include <iostream>
using namespace std;
class TestClass // class head
// class body
{
public: // 访问级别 public(大部分函数)、private(一般数据放在这里)、proteted
TestClass(int a = 0, int b = 0) // 构造函数重载、参数默认值
: _a(a), _b(b) // 构造函数的特殊赋值方法(初值列、初始列)这里严格来讲不是赋值而是初始化
{
// _a = a; _b = b; // 该方法与初值列赋值作用相同,但时间不同,一个在初始化、一个在赋值阶段。在这里赋值时间点会晚一点
}
// 一般构造函数在public作用域,但也可以放在private中,经典案例就是单例模式
// TestClass(): _a(0), _b(0) {} 该构造函数与上面的构造函数冲突了
void setA(const int &a) {this->_a = a;} // 尽可能多的使用引用传参,并搭配const使用,节省数据传递的消耗
void setB(const int &b) {this->_b = b;} // 引用传递一般四个字节,一般对象都大于四个字节所以大部分情况下传递引用可以节省时间
inline int getA() const {return this->_a;} // inline 内联函数 最终由编译器决定是否可以构成内联函数
int getB() const {return this->_b;} // 记得在必要的时候加上const,只要没有修改值的函数建议都加上const;否则const对象无法调用非const函数
/**
* 友元
**/
friend ostream& operator<<(ostream &os, const TestClass &x) // 友元
{
os << '(' << x.getA() << " " << x.getB() << ')';
return os;
}
// friend void set_show(int x, A &a); // 友元函数
// friend class FriendClass; // 友元类
// friend void FriendClass::set_show(int x, A &a); // 友元成员函数
// 两个相同类的Object 互为友元,所以该函数中可以直接访问temp的private作用域数据
void CloneFromOtherA(const TestClass &temp){
this->_a = temp._a;
this->_b = temp._b;
}
/**
* 操作符重载
*/
inline TestClass& operator += (const TestClass &x)
{
return _doapl(this, x);
}
protected:
friend TestClass& _doapl(TestClass* ths, const TestClass& r); // 返回一个相加的对象
private:
int _a;
int _b;
};
/**
* 定义在类外的操作符重载
* https://www.runoob.com/cplusplus/cpp-overloading.html
* 下面为不可重载操作符
.:成员访问运算符
.*, ->*:成员指针访问运算符
:::域运算符
sizeof:长度运算符
?::条件运算符
#: 预处理符号
**/
TestClass& _doapl(TestClass* ths, const TestClass& r)
{
ths->_a += r._a;
ths->_b += r._b;
return *ths; // 该对象不是在函数体中创建的临时对象,所以可以返回引用回去
}
inline TestClass operator +(const TestClass &x, const TestClass &y) // 使用环境为 A + B
{
// 这里的返回值是函数体中的临时对象,所以返回值不能返回引用
return TestClass(x.getA() + y.getA(), x.getB() + y.getB());
}
inline TestClass operator +(const TestClass &x, int y) // 使用环境为 A + 1
{
return TestClass(x.getA() + y, x.getB());
}
inline TestClass operator +(int y, const TestClass &x) // 使用环境为 1 + A
{
return TestClass(x.getA() + y, x.getB());
}
inline TestClass operator + (const TestClass &x) // 正号 正100
{
return TestClass(x.getA(), x.getB());
}
inline TestClass operator - (const TestClass &x) // 负号 负100
{
return TestClass(-x.getA(), -x.getB());
}
inline bool operator == (const TestClass &x, const TestClass &y) // A == B
{
return x.getA() == y.getA() && x.getB() == y.getB();
}
inline bool operator == (const TestClass &x, const int &y) // A == 1
{
return x.getA() == y && x.getB() == 0;
}
inline bool operator == (const int &x, const TestClass &y) // 1 == A
{
return x == y.getA() && y.getB() == 0;
}
#endif
#ifndef __TESTCLASS_POINTER__
#define __TESTCLASS_POINTER__
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
/**
A = B 拷贝赋值 编译器自动生成
A = String(B) 拷贝构造 编译器自动生成
如果Class不存在指针,编译器自动生成问题不大
如果Class存在指针,则拷贝为浅拷贝,即两个Object的成员对象指针指向同一块地址空间
所以如果Class带指针,最好不要使用编译器生成的拷贝构造和拷贝赋值
*/
class String
{
public:
String(const char* cstr = nullptr);
String(const String& str);
String& operator =(const String &str);
~String();
char* get_c_str() const {return m_data;}
private:
char* m_data;
};
String::String(const char* cstr)
{
if (cstr) {
m_data = new char[strlen(cstr) + 1];
strcpy(m_data, cstr);
}
else{
m_data = new char[1];
*m_data = '\0';
}
}
String::~String(){
delete[] m_data;
}
String::String(const String& str)
{
m_data = new char[strlen(str.m_data) + 1];
strcpy(m_data, str.m_data);
}
String& String::operator=(const String &str)
{
if (this == &str) {
// 检测自我赋值 如果不做此处理 后续操作会自己删掉自己的内容 导致问题
return *this;
}
delete[] m_data;
m_data = new char[strlen(str.m_data) + 1];
strcpy(m_data, str.m_data);
return *this;
}
ostream& operator << (ostream& os, const String &str)
{
os << str.get_c_str();
return os;
}
/**
* 建议理解new的底层操作:1.申请内存 2. 类型转换 3. 构造函数
void *mem = operator new (sizeof(String)); // 底层调用malloc()
pc = static_cast<String*>(mem);
pc->String::String("hello");
* delete的底层操作
String::~String(pc); // 调用析构函数
operator delete(pc); // 底层调用free()释放内存
**/
#endif
int main()
{
String s1("hello");
std::cout << s1 << endl;
system("pause");
return 0;
}
delete[] ptr;
delete数组时需要使用delete[]
如果有中括号[],系统知道要删除多个
如果没有中括号,系统不知道要删除多个,因此数组中只有第一个执行了析构,其他的都没有析构
如果成员对象中没有new出来的指针,其实delete也可以,如果有就会内存泄漏
#include <iostream>
using namespace std;
#ifndef __STATIC_STU__
#define __STATIC_STU__
class StaticStuClass
{
public:
static int GetA(const StaticStuClass &x) { return x.m_a; }
static int GetB(const StaticStuClass &x) { return x.m_b; }
static void SetC(const int &val) { s_C = val; } // static函数只能处理static的成员对象,普通数据和普通函数不能调用
static int GetC() { return s_C; }
static StaticStuClass &getInstance()
{
static StaticStuClass self;
return self;
}
public:
void setup()
{
}
StaticStuClass(int _a = 2, int _b = 2) : m_a(_a), m_b(_b)
{
}
~StaticStuClass()
{
}
int getA() const { return this->m_a; }
int getB() const { return this->m_b; }
private:
int m_a, m_b;
static int s_C; // 不管存在多少个StaticStuClass对象,s_C数据只存在一份,m_a、m_b一个对象就有一个
};
int StaticStuClass::s_C = 0; // static的成员对象记得初始化,否则容易报错
#endif
int main()
{
StaticStuClass cls(1, 2);
StaticStuClass::SetC(1);
StaticStuClass::getInstance().setup();
std::cout << cls.getA() << " " << StaticStuClass::GetA(cls) << std::endl;
system("pause");
return 0;
}
#ifndef __Inheritance_Composition_Delegation__
#define __Inheritance_Composition_Delegation__
#include <iostream>
#include <queue>
#include <deque>
using namespace std;
/**
* Composition 复合
* MyQueue 拥有 deque 作为组件,MyQueue因此也拥有deque的功能,所以MyQueue的很多功能可以基于deque改装一下即可
* 构造由内到外:先调用Component的默认构造函数,然后再执行自己的
* 析构由外而内:首先执行自己析构函数,然后再调用Component的析构函数
*/
template <typename T>
class MyQueue
{
public:
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
reference front() { return c.front(); }
reference back() { return c.back(); }
void push(const value_type &x) { c.push_back(x); }
void pop() { c.pop_front(); }
protected:
std::deque<T> c;
};
/**
* delegation 委托 Composition By Reference
* MyString拥有的是一个指针,而不是Component的实体
* 一种用法:一个StringRep可以同时被多个MyString拥有(可以搭配copy on write写时复制,防止某个MyString对StringRep的修改影响了其他使用该StringRep的MyString)
* 一种用法:单纯修改MyString中StringRep指针指向的对象就可以修改MyString的功能,而不用修改MyString对外暴露的接口
*/
class StringRep;
class MyString
{
public:
MyString();
MyString(const char *s);
MyString(const MyString &s);
MyString &operation = (const MyString &s);
~MyString();
private:
StringRep *rep;
};
class StringRep
{
freind class MyString;
StringRep(const char *str);
~StringRep();
int count;
char *rep;
};
/**
* Inheritance 继承 表示 is-a 的关系
* 继承分为public、private、proteted三种,对应访问父类的作用域不同
* 构造由内而外:首先调用基类的构造函数,然后才执行子类的构造
* 析构由外到内:首先调用子类的析构函数,然后才执行基类的析构
* 基类的析构函数必须是virtual,否则容易出现内存泄漏
* non-virtual函数:不希望派生类override的函数
* virtual函数:希望派生类override的函数,并且给出了默认定义
* pure virtual 函数:希望派生类override的函数,并且没有给出默认定义
*
* ————————————————————————————————————————————————————————————————
*
* 一种用法:搭建框架,由基类定好一个基本框架,并将变更的地方设定为虚函数/纯虚函数,由使用者继承并实现
**/
class Animal // 基类
{
private:
public:
Animal();
Animal(int a, int b);
virtual ~Animal();
void dead(); // 普通函数
virtual void run(); // 虚函数
virtual void eat() = 0; // 纯虚函数
};
class Cat : public Animal // 子类
{
private:
public:
Cat(); // 调用基类默认的构造函数
Car(int a, int b) : Animal(a, b); // 调用指定的构造函数
virtual ~Cat();
};
#endif
#include <iostream>
#include <cstring>
using namespace std;
#ifndef __conversion_function__
#define __conversion_function__
/**
* 转换函数
* 一般来说转换数据类型到时候不会修改数据内容,所以需要一般要加上const
* 告诉编译器在需要将该类型转换成指定类型的时候使用该方法转换
**/
class Fraction
{
public:
Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) {}
// 转换函数,告诉编译器在需要将Fraction转换成double的时候使用该方法转换
// 因为是转换函数,其内部的值不能发生修改,所以需要const
operator double() const
{
return (double)(m_numerator * 1.0 / m_denominator);
}
operator string() const
{
char numerator[10];
char denominator[10];
memset(numerator, 0, sizeof(numerator));
memset(denominator, 0, sizeof(denominator));
itoa(m_numerator, numerator, 10);
itoa(m_denominator, denominator, 10);
return std::string(std::string(numerator) + std::string("/") + std::string(denominator));
}
private:
int m_denominator; // 分母
int m_numerator; // 分子
};
#endif
int main()
{
Fraction f(3, 5);
// 编译器优先寻找operator (int, Fraction)的操作符重载,没有发现就再查找Fraction能否转成double
std::cout << 1 + f << std::endl;
std::cout << (std::string)f << std::endl;
system("pause");
return 0;
}
/**
* non-explicit-one-argument 只要一个实参就够了的构造函数
* 下面例子中:没有写oprator * (Fraction, int)的操作符重载也不会报错,因为编译器将4通过一个参数的构造变成Fraction(4, 1)类型
* 当构造函数前加上explicit关键字时,编译器不会进行隐式构造函数将4变成Fraction(4, 1)
*
* ——————————————————————————————————————————————————————————————————————————————
* 如果构造函数不存在explicit,且有operator double()转换函数,编译器报错,因为存在二义性即两种方式都可以行得通且没有优先级这么一说
* 如果构造函数存在explicit,且有operator double()转换函数,编译器不报错,因为只有一条路可走,将Fraction转换成double计算
* 如果构造函数不存在explicit,且没有operator double()转换函数,编译器不报错,因为只有一条路可走,将4转换成Fraction计算
**/
class Fraction
{
public:
// 这里构造函数只需要一个参数其实也够
Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) {}
//explicit Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) {}
Fraction operator*(const Fraction &f)
{
return Fraction(f.m_numerator * this->m_numerator, f.m_denominator * this->m_denominator);
}
std::string getString()
{
char numerator[10];
char denominator[10];
memset(numerator, 0, sizeof(numerator));
memset(denominator, 0, sizeof(denominator));
itoa(m_numerator, numerator, 10);
itoa(m_denominator, denominator, 10);
return std::string(std::string(numerator) + std::string("/") + std::string(denominator));
}
private:
int m_denominator; // 分母
int m_numerator; // 分子
};
int main()
{
Fraction f(3, 5);
Fraction f2 = f * 4;
std::cout << f2.getString() << std::endl;
system("pause");
return 0;
}
explicit告诉编译器,不能发生相应的隐式类型转换,只能以显示的方式进行类型转换
#include <iostream>
#include <vector>
using namespace std;
/**
* specialization 模板特化
* 代码来源:https://www.cnblogs.com/dongzhiquan/p/7726379.html
*/
template <class T>
class Compare
{
public:
static bool IsEqual(const T &lh, const T &rh)
{
std::cout << "normal compare" << std::endl;
return lh == rh;
}
};
template <>
class Compare<double>
{
public:
static bool IsEqual(const double &lh, const double &rh)
{
std::cout << "float compare" << std::endl;
return abs(lh - rh) < 10e-6;
}
};
template <>
class Compare<float>
{
public:
static bool IsEqual(const float &lh, const float &rh)
{
std::cout << "double compare" << std::endl;
return abs(lh - rh) < 10e-3;
}
};
/**
* partial specialization 模板偏特化
**/
// 范围上不同的偏特化
// 普通类型和指针类型的特化
template <typename T>
class C
{
public:
C()
{
std::cout << "normal special" << std::endl;
}
};
template <typename U>
class C<U *>
{
public:
C()
{
std::cout << "pointer special" << std::endl;
}
};
// ----------------------------------
// 数量上不同的偏特化,比如这里我觉得vec对bool需要特殊处理,所以做了对bool偏特化
template <typename T, typename Alloc>
class MyVec
{
public:
MyVec()
{
std::cout << "normal vec" << std::endl;
}
};
template <typename Alloc>
class MyVec<bool, Alloc>
{
public:
MyVec()
{
std::cout << "bool vec" << std::endl;
}
};
int main()
{
Compare<double>::IsEqual(1.0, 1.0);
Compare<float>::IsEqual(1.0f, 1.0f);
Compare<int>::IsEqual(1, 1);
C<string> obj1;
C<string *> obj2;
MyVec<int, int> vec1;
MyVec<bool, int> vec2;
system("pause");
return 0;
}
template <typename T,
template <typename U>
class Container>
class XCls
{
private:
Container<T> c;
public:
};
template <typename T>
using Vst = std::vector<T, allocator<T>>;
int main()
{
// 因为vector需要多个模板参数,平时只给一个是因为有默认模板参数,但是在模板模板参数中只传一个对vector来说会报错,所以用了Vst来代替
XCls<std::string, Vst> mylist1;
system("pause");
return 0;
}
template <typename T>
using Vst = std::vector<T, allocator<T>>;
void print()
{
}
template <typename T, typename... Types>
void print(const T &firstArg, const Types &... args)
{
cout << firstArg << endl;
print(args...);
}
int main()
{
print(1, 2, 3, 4, "123");
system("pause");
return 0;
}
std::vector<int> v{1, 2, 3, 4, 5, 6};
for (auto elem : v) // 值传递
{
std::cout << elem << endl;
}
for (auto &elem : v) // 引用方式取值
{
elem *= 2;
}
for (auto elem : v)
{
std::cout << elem << endl;
}