C++的一些疑惑点整理及解决
文章目录
一、lower_bound upper_bound equal_range
操作 | 效果 |
---|---|
upper_bound(key) | 返回键值大于key的第一元素 |
lower_bound(key) | 返回键值大于等于key的第一元素 |
equal_range(key) | 返回键值等于key的元素区间 |
/*
equal_range:接受一个关键词,返回一个pair对组
若关键词存在,第一个迭代器指向第一个与关键词匹配的元素,第二个指向最后一个匹配元素之后的位置
若未找到匹配,则两个迭代器相等,都指向关键词可插入的位置。
*/
二、符号表
三、delete一个类与静态成员变量和静态成员函数的情况
#include <iostream>
using namespace std;
class cla
{
static int n;
int k = 0;
public:
cla() { n++; }
~cla()
{
n--;
cout << "~cla" << endl;
}
static int get_n()
{
return n;
}
};
int cla::n = 0;
int main(int argc, char const *argv[])
{
// cla *p = new cla;
// delete p;
cout << "n=" << cla::get_n() << endl;
//cout << p->Mytry() << endl;
cout << "hhh" << endl;
return 0;
}
1.当delete时,析构就会调用
2.就算没有构建类的时候,也能访问到静态成员变量和静态成员函数
四、::toupper
toupper:是把小写变成大写的函数
::
这个前面为什么没有命名空间或者std?
问了一位大佬,;;
这个是全局的命名空间,比std的范围还要大,所以toupper可以在外部直接使用,在局部需要加上::
五、push和reserve
push会新建容器,把之前存储的类,进行拷贝构造
emplace是原地构造,不需要吧之前存储的类拷贝构造,
这两者的共同点是,本次传入的类需要拷贝构造
六、多态的再次理解(基类指针和派生类指针)
多态的效果:
多态:多种形态,使用多态,当基类指针指向基类对象的时候,期待其按照基类的方法做事,当基类指针指向派生类对象的时候,就按照派生类的方法做事,实现同一个接口,多种方法,它有多种形态,这种现象称为多态。
多态的条件:
多态的条件:
1.要有继承
2.要有虚函数重写(发生在不同的作用域,函数原型相同)
3.基类指针指向派生类对象
基类指针和派生类指针
1 基类指针指向基类对象(正常使用)
2 派生类指针指向派生类对象(正常使用,不管是不是虚函数,调用的都是派生类的函数)
3 基类指针指向派生类对象(多态的体现,虚函数的话调用的是派生类的,非虚函数的话调用的是基类的)
指针不能使用只存在于派生类而不存在于基类中的元素,也就是指向的是派生类中基类的部分
4 派生类指针指向基类对象(一般不要这么用)
静态联编
.如果以一个基础类指针指向一个衍生类对象(派生类对象),那么经由该指针只能访问基础类定义的函数
基类与派生类的指针和成员函数调用原理
2.如果以一个衍生类指针指向一个基础类对象,必须先做强制转型动作(explicit cast),这种做法很危险,也不符合生活习惯,在程序设计上也会给程序员带来困扰。(一般不会这么去定义)
3.如果基础类和衍生类定义了相同名称的成员函数(非虚函数),那么通过对象指针调用成员函数时,到底调用哪个函数要根据指针的类型(基类指针or派生类指针)来确定,而不是根据指针实际指向的对象类型确定。
4.如果基础类和衍生类定义了相同名称的成员函数(虚函数virtual),那么通过对象指针调用成员函数时,到底调用哪个函数要根据指针实际**指向的对象类型(**基类对象or派生类对象)来确定,而不是根据指针的类型确定。
七、基类对象和派生类对象之间的赋值关系
基类对象和派生类对象之间的赋值关系具体是指:基类的对象可不可以赋值给子类对象或者子类对象可不可以赋值给基类对象。
一般来说,只有派生类的对象可以赋值给基类的对象,反之,则不可以。例如:
Father a ; // 基类对象
Son b ; // 派生类对象
a = b ; // 可以
b = a ; // 不可以
为什么派生类对象可以给基类对象赋值呢?反之则不可以呢?这是**因为基类对象a的成员比派生类对象b的成员少。所以基类对象赋值给派生类对象时会出错**。上面是一种情况,还有另外一种情况:
Father a ; // 基类对象
Son b ; // 派生类对象
Father *pa = &b ; // 可以
Son *pb = &a ; // 不可以
上面的指针赋值也就是说:**基类的指针可以指向派生类对象,但是反过来则不行,派生类的指针不可以指向基类的指针**。这是为什么呢?这是因为派生类的对象所占的存储空间通常要比基类的对象大,原因就是派生类除了继承基类的成员之外,还拥有自己的成员,所以**基类的指针操作派生类的对象时,由于基类指针会向操作基类对象那样操作派生类对象,而基类对象所占用的内存空间通常小于派生类对象,所以基类指针不会超出派生类对象去操作数据。**
**同样的道理,基类的引用可以作为派生类对象的别名,但是反过来则不行,派生类的引用不可以作为基类对象的别名**。例如:
Father a ; // 基类对象
Son b ; // 派生类对象
Father &f = b ; // 可以
Son &s = a ; // 不可以
八、工厂模式
factory是来通过对于的工厂创建apple等水果
在创建好后,用fruit来接,并且调用
九、抽象工厂模式和工厂模式的区别
汽车可以分为轿车、SUV、MPV等,也分为奔驰、宝马等。我们可以将奔驰的所有车看作是一个产品族,而将宝马的所有车看作是另一个产品族。分别对应两个工厂,一个是奔驰的工厂,另一个是宝马的工厂。与工厂方法不同,奔驰的工厂不只是生产具体的某一个产品,而是一族产品(奔驰轿车、奔驰SUV、奔驰MPV)。“抽象工厂”的“抽象”指的是就是这个意思。 即相比于工厂方法,抽象工厂定义了一系列的产品,而不是一个产品。
十、原型模式下的protected:
#include <iostream>
#include <string>
using namespace std;
class Person
{
protected:
string name;
int age;
public:
Person()
{
}
Person(string s,int a)
{
this->name = s;
this->age = a;
}
public:
virtual void show() = 0;
virtual Person *clone() = 0;
};
class Student :public Person
{
private:
int id;
public:
Student()
{
}
Student(string s,int a,int i):Person(s,a)
{
this->id = i;
}
void show()
{
cout << "name = " << name << " age = " << age << " id = " << id << endl;
}
Person *clone()
{
Student *tmp = new Student;
*tmp = *this;
return tmp;
}
};
int main(int argc, char const *argv[])
{
Person *p1 = new Student("zzz", 18, 21);
Person *p2 = p1->clone();
p2->show();
return 0;
}
十一、设计模式
1.单例模式-懒汉式
保证一个类只能生成唯一的实例对象,也就是说,在整个程序中,只存在一个实例对象。
代码特点:
定义两个static,一个用作计数,一个用作对象指针
编程思想;
第一次调用会创建,之后再调用会计数+1
第一Relsease(函数),进行减1,直到0时,进行delete
2.单例模式-饿汉式
代码特点:
和懒汉式很像。在对对象指针初始化时,就创建对象了
编程思想:
和懒汉类似
3.工厂模式
代码特点:
工厂 new某种水果
水果 show
编程思想:
factory是来通过对于的工厂创建apple等水果
在创建好后,用fruit来接,并且调用
4.抽象工厂模式
代码特点:
class Factory
{
public:
virtual Fruit *CreateApple() = 0;
virtual Fruit *CreateBanana() = 0;
virtual Fruit *CreatePear() = 0;
};
编程思想:
和工厂模式相似,不过是对工厂的进一步抽象
5.建造者模式
房子
代码特点:
class Builder
{
protected:
House *house;
public:
virtual void Constructor() = 0;
};
class CommonBuilder :public Builder
{
public:
CommonBuilder(House *h)
{
house = h;
}
void Constructor()
{
house->SetDoor("DOOR");
house->SetWall("WALL");
house->SetWindow("WINDOW");
}
};
编程思想:
将一个复杂的构建与它的表示进行分离,使得同样的构造过程可以创建不同的表示
6.原型模式
代码特点:
Person *clone()
{
Student *tmp = new Student;
*tmp = *this;
return tmp;
}
编程思想:
对子类对象进行克隆,基类有多态
7.组合模式
代码特点:
class Dir:public iFile
{
private:
string name;
list<iFile *> *l;
public:
编程思想:
将一个派生类组合到另一个派生来,内部有容器,对派生类进程存储,容器内存的是父类指针
8.代理模式
代码特点:
代理类,定义了父类指针指向被代理的
class TaoBao:public Sale
{
private:
Sale *sale;
public:
TaoBao(Sale *s)
{
sale = s;
}
void SaleBook()
{
cout << "网上卖书" << endl;
sale->SaleBook();
}
};
编程思想:
一个类实现的功能,部分或者全部可以由其他类代理(实现),父类是对功能的抽象
9.装饰模式
代码特点:
定义父类指针,调用这个指针的function
class PhotoPhone:public Phone
{
private:
Phone *phone;
public:
PhotoPhone(Phone *p)
{
phone = p;
}
void function()
{
phone->function();
cout << "相机的功能" << endl;
}
};
编程思想:
不断的对类进行包装,调用上一个function
10.适配器模式
代码特点:
class Adapter:public Current
{
private:
Current *current;
public:
Adapter(Current *c)
{
current = c;
}
int GetCurrent()
{
return 12;
}
};
编程思想:
使之前的类,适配成需要的接口(参数)
11.享元模式
代码特点:
class FwFactory
{
private:
multimap<int, Student*> *m;
public:
FwFactory()
{
m = new multimap<int, Student *>;
}
~FwFactory()
{
while (!m->empty())
{
Student *tmp = m->begin()->second;
delete tmp;
m->erase(m->begin());
}
delete m;
}
void GetPerson(int id)
{
string name;
int age;
multimap<int, Student *>::iterator it;
it = m->find(id);
if (it == m->end())
{
cout << "input the name age..." << endl;
cin >> name >> age;
Student *s = new Student(name, age, id);
m->emplace(make_pair(id, s));
}
else
{
Student *s = it->second;
cout << s->GetName() << " " << s->GetAge() << " " << s->GetId() << endl;
}
}
};
编程思想:
主要通过与其它类对象共享数据来减少内存的使用
适用情况:有大量对象需要重复创建的时候,或者以共享内存的方式
12.桥接模式
代码特点:
class Soft //实现两种从产品的链接,把不同的产品剥离出来,耦合度低
{
protected:
Phone *phone;
public:
virtual void func() = 0;
};
class QQ :public Soft
{
public:
QQ(Phone *p)
{
phone = p;
}
void func()
{
phone->func();
cout << "this is QQ" << endl;
}
};
编程思想:
将抽象和实现分离,使他们可以独立变化。 基于类的最小设计原则
13.外观模式
代码特点:
class Facade
{
private:
SystemA *a;
SystemB *b;
SystemC *c;
public:
Facade()
{
a = new SystemA;
b = new SystemB;
c = new SystemC;
}
~Facade()
{
delete a;
delete b;
delete c;
}
void func()
{
a->func();
b->func();
c->func();
}
};
编程思想:
提供一个简单一致的界面,为了系统中统一的一套接口
14.观察者模式
代码特点:
class Observer //观察者 员工
{
public:
virtual void Subscribe(Infom *i) = 0; //订阅
virtual void UnSubscribe(Infom *i) = 0; //取消订阅
virtual void Update(string str) = 0; //更新状态
};
class Infom //通知者 秘书
{
public:
virtual void Add(Observer *o) = 0;
virtual void Remove(Observer *o) = 0;
virtual void Notify(string str) = 0;
};
class Secretary :public Infom
{
private:
list <Observer *> l;
public:
void Add(Observer *o)
{
l.emplace_back(o);
}
void Remove(Observer *o)
{
l.remove(o);
}
void Notify(string str)
{
for (auto &it : l)
{
it->Update(str);
}
}
};
编程思想:
当对象存在一对多的关系的时候,使用观察者模式,当一个对象被修改的时候,则会自动通知 依赖他的所有对象。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理