文章目录
- 1.cout << hex 会把之后所有整数的输出全部改为 16 进制,如果想显示 0x 需要添加 cout << showbase 同样是作用于全局的
2.二进制表示方法 引入头文件<bitset> 使用函数 bitset<8>(num) 这里 8 表示的二进制位制
- cout << fixed << setprecision(3) << double << endl; #保留三位小数 cout << setprecision(3) << setw(7) << setfill('*') << left << num << endl; 保留三位有效数字 保留 3 位有效数字 从整数部分开始算(double 类型)(int 类型不受影响) setw 宽度 setfill 填充的字符 left 默认是 right (这里除了 setw setprecision 是临时的 其他都是永久变化的,之后再验证) cout <<setprecision(3)<< setiosflags(ios::fixed) << setw(7) << setfill('*') << left << num << endl; setiosflags(ios::fixed)把有效数字 改成了 保留三位小数 类似于计算器中 fixed 模式 setiosflags(ios::scientific)指数显示 清除格式 std::resetiosflags(std::ios::scientific) setiosflags(ios::left)左对⻬ 注意:默认添加了一个指数显示 (笔误写错了) setiosflags(ios::right)右对⻬ 注意:默认添加了一个指数显示 setiosflags(ios::skipws)忽略前导空白 iss >> std::resetiosflags(std::ios::skipws) >> c1 >> c2 >> c3; setiosflags(ios::uppercase)十六进制数大写输出 cout << setiosflags(ios::uppercase) << hex << num << endl; setiosflags(ios::lowercase)十六进制数小写输出 这个直接运行会报错 建议不要运行 setiosflags(ios::showbase)按十六/八进制输出数据时,前面显示前导符 0x/0;cout << setiosflags(ios::showbase) << hex_num << endl; (全都是同理) 4 注意 for 循环的终值如何开始条件 i 是满足终值条件 那么循环一次都不能运行 5do while 循环中 while(执行条件); 后面有一个冒号
- #include <iostream> int main() { int i = 1; switch (i) { case 1: std::cout << 1; break; case 2: std::cout << 2; break; default: std::cout << 0; } return 0; }
- #include <iostream> int main() { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { std::cout << "i=" << i << ", j=" << j << std::endl; if (i == 1 && j == 1) { goto exit_loop; // 直接跳出两层循环 } } } exit_loop: std::cout << "已跳出循环" << std::endl; return 0; } 8. 循环语句和switch语句不能出现在内联函数中 9 常量指针指针常量 • 指向常量的指针 • const <类型> * <指针变量> • <类型> const * <指针变量> • 指针所指向的对象不可以修改 • 常量指针 • <类型> * const <指针变量> • 指针本身不能修改,但其指向的对象可以修改 10 string的成员函数的用法 string(const char* s) string(size_t n, char c) char& operator[] 不进行边界检查 char& at(size_t pos) 进行边界检查 c_str() 返回一个指向以空字符结尾的 C 风格字符串的指针 front() and back() size() and length() empty() reserve(size_t n): 预留至少能容纳 n 个字符的内存空间 shrink_to_fit(): 请求系统释放未使用的内存 operator+= str.append 添加一个字符串到结尾, 但char不行, 但是 +=可以添加char到字符串末尾 (or just 转换 char 到 string(n,char)) str.assign 用新的字符串替换当前字符串的内容 string& erase(size_t pos = 0, size_t len = npos)删除从位置 pos (索引)开始的 len 个字符 string.erase()=string.clear() find成员函数 find 如果没有找到, 返回 string::npos size_t 类型的一个特殊值,表示不存在 rfind: 第一个参数 string, and 第二个参数是 pos (size_t type) find_first_of find_last_of find_first_not_of find_last_not_of 都是从str中任意一个字符char 找到符合 或者不符合的第一个 > < compare成员函数 切片 substr(size_t pos , size_t len = npos)
- 1.按位与(&) 2.按位或(|) 3.取反(~) 4.按位异或(^) 相同为0 不同为1 5左移<< 右移>> 例题base64 注意点:因为+的优先级是高于<< 或者>>运算符 所以 一定一定要记得加括号 在左移运算时1被推到左边,需要确保位数一直是8 小技巧:去掉二进制位数为k的 可以将它&(~(1<<k)) 直接把第k位变成0
-
- cin cout cerr(不能被重定向,无缓冲) clog(不能给重定向,能被缓冲)
- cout << setiosflags(ios::showbase | ios::uppercase); 取消操作:cout<<resetiosflags(ios::showbase | ios::uppercase);
- #include <fstream> ifstream oftream fstream 1.– open()的函数原型为: void open(const char * filename,int mode,int prot=filebuf::openprot); ios::in ios::out ios::ate 打开是文件指针定位到文件尾 ios::app 添加模式,所有增加都在文件尾进行 ios::trunc 如果文件已存在则清空原文件 ios::nocreate 如果文件不存在则打开失败 ios::noreplace 如果文件存在则打开失败 ios::binary 二进制文件 ifstream mode默认ios::in oftream mode默认ios::out prot决定文件的访问方式 -0普通 -1只读 -2 隐含 -4系统 (ios::in|ios::binary以只读方式打开二进制文件) 2.文件的读写 文件输入流成员函数 get >>+“读取空白字符的功能” read 文件输出流成员函数 open put(一个字符) write(内存) seekp tellp (操作文件流的内部指针) close 3.字符串流 #include <iostream> #include <sstream> #include <string> using namespace std; int main() { // 数字转字符串:使用输出字符串流自动管理缓冲区 ostringstream os; double d1 = 123.231; os << d1; string str = os.str(); cout << str << endl; // 输出: 123.231 // 字符串转数字:使用输入字符串流解析数据 istringstream is("1 2.5 A"); int num1; double num2; char num3; is >> num1 >> num2 >> num3; cout << num1 << " " << num2 << " " << num3 << endl; // 输出: 1 2.5 A return 0; }
-
- istream& getline(istream& is, string& str, char delim); // 带分隔符 istream& getline(istream& is, string& str); // 默认分隔符为 '\n' 默认分隔符'\n' getline(cin,line) 自定义分隔符'#' getline(cin,line,'#') 注意于cin>>的冲突 cin 默认忽略前导空白 但会留在换行在缓存区可以用cin.get()接收这个空行 也可以cin.ignore(numeric_limits<streamsize>::max(), '\n');来清空缓存区的换行符 第二种用法isstream的 istream& istringstream::getline(char* s, streamsize n, char delim); // 带分隔符 istream& istringstream::getline(char* s, streamsize n); // 默认分隔符 '\n'
- vector<int>vec(10,1) vec内有10个1 从数组中初始化vec int a[5] = {1,2,3,4,5} vector<int> vec5(a,a+5)
- Namespace
- 作用:对源程序编译器前做一些处理
宏定义 #define
文件包含 #include
条件编译 #if #else #endif等
- 不带参数的宏 #define PI 3.14159 同时#undef可终止宏名作用域 #undef 宏名 宏定义允许嵌套 带参数的宏 #include <iostream> using namespace std; // 定义宏计算两个数的最大值 #define MAX(a, b) ((a)>(b)?(a):(b)) int main() { int x, y, max_val; // 变量名避免使用 max(与标准库冲突) cout << "input two numbers: "; cin >> x >> y; max_val = MAX(x, y); // 使用宏计算最大值 cout << "max=" << max_val << endl; // 修正引号为英文引号 return 0; } 带参的宏与函数的区别
- 头文件卫士如果多个.cpp包含.h 编译会出错 报告:XX重复定义 条件编译 #ifdef 标识符 程序段1; [#else 程序段2;] #endif 和ifdef作用相反的 ifndef 还有 #if 常量表达式 正常就是 #ifndef 标识符 #define 标识符 程序 ----- 代码 #endif
- 特性 Debug 模式 Release 模式 优化级别 通常不进行优化(如 -O0),方便调试。 启用高级优化(如 -O2、-O3),提升性能。 调试信息 包含完整调试信息(如行号、变量名),文件更大。 移除调试信息,文件更小。 断言(Assert) 启用断言检查,帮助定位逻辑错误。 通常禁用断言(提高性能)。 内存检查 可能启用内存越界检测(如 MSVC 的 /RTC)。 不启用内存检测,减少开销
- 基本的就不说了
成员函数 允许声明重载函数 和 带缺省形参值的函数
内联成员函数 inline
注意:声明时不要写inline 在实现的时候再写inline
- 类外:可以使用 . 来访问public属性的成员
- new 与 delete Ticket *a = new Ticket; delete a;
- 注意:友元函数并非类的成员函数 需要用.访问对象 友元函数其实只是一个声明 只要在类中声明 然后再函数实现时不要出现
- . * :: ?: sizeof() 不可以重载 1.通过类成员函数重载 2.使用友元函数重载运算符 (通常多一个参数) 有如下限制: = [] () new delete 不能重载 注意:重载的赋值运算符不能继承 而且 不能将运算符重载函数声明为虚函数 << 这个只能用友元函数重载 [] 必须用类的成员函数重载
- 不允许指定返回值 不可以带参数 不能重载 系统自动调用
- 如果没有 编译器会自己生成一个拷贝构造函数
- 只对对象执行读操作的函数 对象的常引用 只能调用常成员函数
- 该类的所有对象维护该成员的同一个拷贝 必须在类外定义和初始化,用(::)来指明所属的类。 在编译期分配存储空间,其它成员数据在运行期分配存储空间 可以通过类名限定直接引用public静态成员数据,无需对象
- 静态成员函数可以通过类名限定直接调用或通过对象调用 静态成员函数可以直接使用类的静态成员数据与静态成员函数 静态成员函数不可以直接使用类的非静态成员数据与非静态成员函数 静态成员函数没有this指针 举例: #include <iostream> using namespace std; class Point { public: // 构造函数 Point(int xx = 0, int yy = 0) { X = xx; Y = yy; countP++; } // 拷贝构造函数 Point(const Point &p) { X = p.X; Y = p.Y; countP++; } int GetX() const { return X; } int GetY() const { return Y; } // 静态成员函数 static void GetC() { cout << " Object id=" << countP << endl; } private: int X, Y; static int countP; // 静态数据成员 }; // 初始化静态成员变量 int Point::countP = 0; int main() { // 标准main函数返回类型 Point A(4, 5); // 声明对象A cout << "Point A," << A.GetX() << "," << A.GetY(); A.GetC(); // 输出对象号,对象名引用 Point B(A); // 声明对象B cout << "Point B," << B.GetX() << "," << B.GetY(); Point::GetC(); // 输出对象号,类名引用 return 0; }
-
- 公有继承 基类的public和protected成员的访问属性在派生类中保持不变,但基类的private成员不可访问 派生类中的成员函数可以直接访问基类中的public和和protected成员 ,但不嗯嗯访问基类的private成员 通过派生类的对象只能访问基类的public成员 class Rectangle:public Point {...}; 私有继承 基类的public和protected成员都以private身份出现在派生类中,但基类的private成员不可访问。 派生类中的成员函数可以直接访问基类中的public和protected成员,但不能访问基类的private成员。 通过派生类的对象不能访问基类中的任何成员。 保护继承 基类的public和protected成员都以protected身份出现在派生类中,但基类的private成员不可访问。 派生类中的成员函数可以直接访问基类中的public和protected成员,但不能访问基类的private成员。 通过派生类的对象不能访问基类中的任何成员 重点:protected成员的特点与作用 对于建立其所在类对象的模块来说 相当于private 对于其派生类来说 相当于public
- 首先 基类的构造函数不能被继承 需要在派生类中自行声明 自动执行基类和派生类的构造函数(当派生类的对象时) 可以通过派生类构造函数显式调用基类的构造函数,从而为基类构造函数传递参数 基类与派生类的构造函数调用析构函数的过程与构造函数相反 基类与派生类的名字冲突: 不使用基类名限定 就是指的是派生类的同名成员 二义性问题(?) 在多继承时,基类与派生类之间,或基类之间出现同名成员时,将出现访问时的二义性(不确定性)——采用虚函数或支配(同名覆盖)原则来解决。 当派生类从多个基类派生,而这些基类又从同一个基类派生,则在访问此共同基类中的成员时,将产生二义性——采用虚基类来解决。 赋值兼容规则 #include <iostream> // 基类 B0 声明 class B0 { public: // 公有虚成员函数,为多态提供基础 virtual void display() { std::cout << "B0::display()" << std::endl; } }; // 公有继承 B0,B1 是 B0 的派生类 class B1 : public B0 { public: // override 显式标记覆盖基类虚函数,增强代码可读性与规范性 void display() override { std::cout << "B1::display()" << std::endl; } }; // 公有继承 B1,D1 是 B1 的派生类,间接继承 B0 class D1 : public B1 { public: // override 标记覆盖基类虚函数 void display() override { std::cout << "D1::display()" << std::endl; } }; // 接收 B0 指针,利用多态调用不同派生类的 display 函数 void fun(B0* ptr) { // 通过基类指针调用虚函数,运行时会根据实际对象类型动态绑定 ptr->display(); } int main() { B0 b0; // 定义 B0 类对象 B1 b1; // 定义 B1 类对象 D1 d1; // 定义 D1 类对象 // 传入不同对象地址,触发多态 fun(&b0); // 调用 B0 的 display fun(&b1); // 调用 B1 的 display fun(&d1); // 调用 D1 的 display return 0; }
- virtual只要在基函数写就行 但推荐在派生类的成员函数也加上virtual
devcpp 需要在编译时 添加这个来支持 c++11 -std=c++11
随机数种子随时间变化的函数
#include <ctime>
srand((unsigned int)time(NULL));
int num = rand()%100 + 1 #生成1~100的随机数
1.cout << hex 会把之后所有整数的输出全部改为 16 进制,如果想显示 0x 需要添加 cout << showbase 同样是作用于全局的
2.二进制表示方法 引入头文件<bitset> 使用函数 bitset<8>(num) 这里 8 表示的二进制位制
- cout << fixed << setprecision(3) << double << endl; #保留三位小数
- cout << setprecision(3) << setw(7) << setfill('*') << left << num << endl; 保留三位有效数字
- 保留 3 位有效数字 从整数部分开始算(double 类型)(int 类型不受影响) setw 宽度 setfill 填充的字符 left 默认是 right (这里除了 setw setprecision 是临时的 其他都是永久变化的,之后再验证)
- cout <<setprecision(3)<< setiosflags(ios::fixed) << setw(7) << setfill('*') << left << num << endl;
- setiosflags(ios::fixed)把有效数字 改成了 保留三位小数 类似于计算器中 fixed 模式
- setiosflags(ios::scientific)指数显示 清除格式 std::resetiosflags(std::ios::scientific)
- setiosflags(ios::left)左对⻬
注意:默认添加了一个指数显示 (笔误写错了)
setiosflags(ios::right)右对⻬ 注意:默认添加了一个指数显示
setiosflags(ios::skipws)忽略前导空白 iss >> std::resetiosflags(std::ios::skipws) >> c1 >> c2 >> c3;
- setiosflags(ios::uppercase)十六进制数大写输出 cout << setiosflags(ios::uppercase) << hex << num << endl;
setiosflags(ios::lowercase)十六进制数小写输出 这个直接运行会报错 建议不要运行
setiosflags(ios::showbase)按十六/八进制输出数据时,前面显示前导符 0x/0;cout << setiosflags(ios::showbase) << hex_num << endl; (全都是同理)
setiosflags(ios::right)右对⻬
setiosflags(ios::skipws)忽略前导空白 iss >> std::resetiosflags(std::ios::skipws) >> c1 >> c2 >> c3;
setiosflags(ios::lowercase)十六进制数小写输出 这个直接运行会报错 建议不要运行
setiosflags(ios::showbase)按十六/八进制输出数据时,前面显示前导符 0x/0;cout << setiosflags(ios::showbase) << hex_num << endl; (全都是同理)
4 注意 for 循环的终值如何开始条件 i 是满足终值条件 那么循环一次都不能运行
5do while 循环中 while(执行条件); 后面有一个冒号
#include <iostream>
int main() {
int i = 1;
switch (i) {
case 1:
std::cout << 1;
break;
case 2:
std::cout << 2;
break;
default:
std::cout << 0;
}
return 0;
}
#include <iostream>
int main() {
int i = 1;
switch (i) {
case 1:
std::cout << 1;
break;
case 2:
std::cout << 2;
break;
default:
std::cout << 0;
}
return 0;
}
#include <iostream>
int main() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
std::cout << "i=" << i << ", j=" << j << std::endl;
if (i == 1 && j == 1) {
goto exit_loop; // 直接跳出两层循环
}
}
}
exit_loop:
std::cout << "已跳出循环" << std::endl;
return 0;
}
#include <iostream>
int main() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
std::cout << "i=" << i << ", j=" << j << std::endl;
if (i == 1 && j == 1) {
goto exit_loop; // 直接跳出两层循环
}
}
}
exit_loop:
std::cout << "已跳出循环" << std::endl;
return 0;
}8. 循环语句和switch语句不能出现在内联函数中
9 常量指针指针常量
• 指向常量的指针
• const <类型> * <指针变量>
• <类型> const * <指针变量>
• 指针所指向的对象不可以修改
• 常量指针
• <类型> * const <指针变量>
• 指针本身不能修改,但其指向的对象可以修改
10 string的成员函数的用法
- string(const char* s)
- string(size_t n, char c)
- char& operator[] 不进行边界检查 char& at(size_t pos) 进行边界检查
- c_str() 返回一个指向以空字符结尾的 C 风格字符串的指针
- front() and back()
- size() and length()
- empty()
- reserve(size_t n): 预留至少能容纳 n 个字符的内存空间
- shrink_to_fit(): 请求系统释放未使用的内存
- operator+=
- str.append 添加一个字符串到结尾, 但char不行, 但是 +=可以添加char到字符串末尾 (or just 转换 char 到 string(n,char))
- str.assign 用新的字符串替换当前字符串的内容
- string& erase(size_t pos = 0, size_t len = npos)删除从位置 pos (索引)开始的 len 个字符 string.erase()=string.clear()
- find成员函数
- find 如果没有找到, 返回 string::npos size_t 类型的一个特殊值,表示不存在
- rfind: 第一个参数 string, and 第二个参数是 pos (size_t type)
- find_first_of find_last_of find_first_not_of find_last_not_of 都是从str中任意一个字符char 找到符合 或者不符合的第一个
- > < compare成员函数
- 切片 substr(size_t pos , size_t len = npos)
1.按位与(&)
2.按位或(|)
3.取反(~)
4.按位异或(^) 相同为0 不同为1
5左移<< 右移>>
例题base64 注意点:因为+的优先级是高于<< 或者>>运算符 所以 一定一定要记得加括号
在左移运算时1被推到左边,需要确保位数一直是8
小技巧:去掉二进制位数为k的 可以将它&(~(1<<k)) 直接把第k位变成0
cin cout cerr(不能被重定向,无缓冲) clog(不能给重定向,能被缓冲)
cout << setiosflags(ios::showbase | ios::uppercase);
取消操作:cout<<resetiosflags(ios::showbase | ios::uppercase);
#include <fstream>
ifstream oftream fstream
1.– open()的函数原型为:
void open(const char * filename,int mode,int prot=filebuf::openprot);
- ios::in ios::out ios::ate 打开是文件指针定位到文件尾
- ios::app 添加模式,所有增加都在文件尾进行
- ios::trunc 如果文件已存在则清空原文件 ios::nocreate 如果文件不存在则打开失败
- ios::noreplace 如果文件存在则打开失败 ios::binary 二进制文件
ifstream mode默认ios::in oftream mode默认ios::out
prot决定文件的访问方式 -0普通 -1只读 -2 隐含 -4系统
(ios::in|ios::binary以只读方式打开二进制文件)
2.文件的读写
文件输入流成员函数
get >>+“读取空白字符的功能”
read
文件输出流成员函数
open put(一个字符) write(内存)
seekp tellp (操作文件流的内部指针)
close
3.字符串流
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main() {
// 数字转字符串:使用输出字符串流自动管理缓冲区
ostringstream os;
double d1 = 123.231;
os << d1;
string str = os.str();
cout << str << endl; // 输出: 123.231
// 字符串转数字:使用输入字符串流解析数据
istringstream is("1 2.5 A");
int num1;
double num2;
char num3;
is >> num1 >> num2 >> num3;
cout << num1 << " " << num2 << " " << num3 << endl; // 输出: 1 2.5 A
return 0;
}
istream& getline(istream& is, string& str, char delim); // 带分隔符
istream& getline(istream& is, string& str); // 默认分隔符为 '\n'
istream& getline(istream& is, string& str, char delim); // 带分隔符
istream& getline(istream& is, string& str); // 默认分隔符为 '\n'默认分隔符'\n' getline(cin,line)
自定义分隔符'#' getline(cin,line,'#')
注意于cin>>的冲突 cin 默认忽略前导空白 但会留在换行在缓存区可以用cin.get()接收这个空行
也可以cin.ignore(numeric_limits<streamsize>::max(), '\n');来清空缓存区的换行符
第二种用法isstream的
istream& istringstream::getline(char* s, streamsize n, char delim); // 带分隔符
istream& istringstream::getline(char* s, streamsize n); // 默认分隔符 '\n'
vector<int>vec(10,1) vec内有10个1
从数组中初始化vec
int a[5] = {1,2,3,4,5}
vector<int> vec5(a,a+5)
vector.push_back()
vector.size() vector.emtpy()
vector.insert(一个指针,插入的元素个数,插入的元素的值) 或者vector.insert(对应位置的指针,插入的元素的数值)
vector.pop_back()
vector.erase(对应位置的指针) 比如vec5.erase(vec5.begin()+3)
Namespace
namespace 名称 {}
名字空间不一定连续 可以累积
名字空间 可以 嵌套
直接访问 ::
使用using 比如:using namespace std;
using namespace 已定义的名字空间的符号
(内部所用 不与其他文件冲突)
// -----MathLib.cpp -----namespace {void swap( double *d1, double *d2 ) { /* ... */}} //函数swap()只在文件MathLib.cpp中可见
标准c++库中的所有组成部分都是在一个被称为std的名字空间中声明和定义的
作用:对源程序编译器前做一些处理
- 宏定义 #define
- 文件包含 #include
- 条件编译 #if #else #endif等
不带参数的宏 #define PI 3.14159
同时#undef可终止宏名作用域 #undef 宏名
宏定义允许嵌套
带参数的宏
#include <iostream>
using namespace std;
// 定义宏计算两个数的最大值
#define MAX(a, b) ((a)>(b)?(a):(b))
int main() {
int x, y, max_val; // 变量名避免使用 max(与标准库冲突)
cout << "input two numbers: ";
cin >> x >> y;
max_val = MAX(x, y); // 使用宏计算最大值
cout << "max=" << max_val << endl; // 修正引号为英文引号
return 0;
}
带参的宏与函数的区别

头文件卫士如果多个.cpp包含.h 编译会出错 报告:XX重复定义
条件编译
#ifdef 标识符程序段1;[#else程序段2;]#endif
和ifdef作用相反的 ifndef 还有 #if 常量表达式
正常就是
#ifndef 标识符#define 标识符程序 ----- 代码#endif
| 特性 | Debug 模式 | Release 模式 |
|---|---|---|
| 优化级别 | 通常不进行优化(如 -O0),方便调试。 |
启用高级优化(如 -O2、-O3),提升性能。 |
| 调试信息 | 包含完整调试信息(如行号、变量名),文件更大。 | 移除调试信息,文件更小。 |
| 断言(Assert) | 启用断言检查,帮助定位逻辑错误。 | 通常禁用断言(提高性能)。 |
| 内存检查 | 可能启用内存越界检测(如 MSVC 的 /RTC)。 |
不启用内存检测,减少开销 |
基本的就不说了
成员函数 允许声明重载函数 和 带缺省形参值的函数
内联成员函数 inline
注意:声明时不要写inline 在实现的时候再写inline
类外:可以使用 . 来访问public属性的成员
new 与 delete
Ticket *a = new Ticket;delete a;
注意:友元函数并非类的成员函数 需要用.访问对象
友元函数其实只是一个声明
只要在类中声明 然后再函数实现时不要出现
. * :: ?: sizeof() 不可以重载
1.通过类成员函数重载
2.使用友元函数重载运算符 (通常多一个参数)
有如下限制: = [] () new delete 不能重载
注意:重载的赋值运算符不能继承 而且 不能将运算符重载函数声明为虚函数
<< 这个只能用友元函数重载
[] 必须用类的成员函数重载
不允许指定返回值 不可以带参数 不能重载 系统自动调用
如果没有 编译器会自己生成一个拷贝构造函数
只对对象执行读操作的函数
对象的常引用 只能调用常成员函数
- 该类的所有对象维护该成员的同一个拷贝
- 必须在类外定义和初始化,用(::)来指明所属的类。
- 在编译期分配存储空间,其它成员数据在运行期分配存储空间
- 可以通过类名限定直接引用public静态成员数据,无需对象
- 静态成员函数可以通过类名限定直接调用或通过对象调用
- 静态成员函数可以直接使用类的静态成员数据与静态成员函数
- 静态成员函数不可以直接使用类的非静态成员数据与非静态成员函数
- 静态成员函数没有this指针
举例:
#include <iostream>
using namespace std;
class Point {
public:
// 构造函数
Point(int xx = 0, int yy = 0) {
X = xx;
Y = yy;
countP++;
}
// 拷贝构造函数
Point(const Point &p) {
X = p.X;
Y = p.Y;
countP++;
}
int GetX() const { return X; }
int GetY() const { return Y; }
// 静态成员函数
static void GetC() {
cout << " Object id=" << countP << endl;
}
private:
int X, Y;
static int countP; // 静态数据成员
};
// 初始化静态成员变量
int Point::countP = 0;
int main() { // 标准main函数返回类型
Point A(4, 5); // 声明对象A
cout << "Point A," << A.GetX() << "," << A.GetY();
A.GetC(); // 输出对象号,对象名引用
Point B(A); // 声明对象B
cout << "Point B," << B.GetX() << "," << B.GetY();
Point::GetC(); // 输出对象号,类名引用
return 0;
}
引用返回在返回的值的类型后面 加一个& ,注意不要返回无效的引用
用在无法成员化的操作符重载中 比如;
friend ostream& operator<<(ostream& o, const Point& d );
构造位置:
- 全局数据区:
全局对象,静态全局对象,静态局部对象,常对象类的静态数据成员也存放在该数据区 - 栈区:
局部对象(根据不同编译器的实现方法,临时对象可能在栈区,也可能在动态存储区,也可能一部分在栈区,一部分在动态存储区) - 动态存储区(也称堆区):用new申请的对象
公有继承
- 基类的public和protected成员的访问属性在派生类中保持不变,但基类的private成员不可访问
- 派生类中的成员函数可以直接访问基类中的public和和protected成员 ,但不嗯嗯访问基类的private成员
- 通过派生类的对象只能访问基类的public成员
class Rectangle:public Point {...};
私有继承
- 基类的public和protected成员都以private身份出现在派生类中,但基类的private成员不可访问。
- 派生类中的成员函数可以直接访问基类中的public和protected成员,但不能访问基类的private成员。
- 通过派生类的对象不能访问基类中的任何成员。
保护继承
- 基类的public和protected成员都以protected身份出现在派生类中,但基类的private成员不可访问。
- 派生类中的成员函数可以直接访问基类中的public和protected成员,但不能访问基类的private成员。
- 通过派生类的对象不能访问基类中的任何成员
重点:protected成员的特点与作用
对于建立其所在类对象的模块来说 相当于private
对于其派生类来说 相当于public
- 首先 基类的构造函数不能被继承 需要在派生类中自行声明
- 自动执行基类和派生类的构造函数(当派生类的对象时)
- 可以通过派生类构造函数显式调用基类的构造函数,从而为基类构造函数传递参数
基类与派生类的构造函数调用析构函数的过程与构造函数相反
基类与派生类的名字冲突: 不使用基类名限定 就是指的是派生类的同名成员
二义性问题(?)
- 在多继承时,基类与派生类之间,或基类之间出现同名成员时,将出现访问时的二义性(不确定性)——采用虚函数或支配(同名覆盖)原则来解决。
- 当派生类从多个基类派生,而这些基类又从同一个基类派生,则在访问此共同基类中的成员时,将产生二义性——采用虚基类来解决。
赋值兼容规则
#include <iostream>
// 基类 B0 声明
class B0 {
public:
// 公有虚成员函数,为多态提供基础
virtual void display() { std::cout << "B0::display()" << std::endl; }
};
// 公有继承 B0,B1 是 B0 的派生类
class B1 : public B0 {
public:
// override 显式标记覆盖基类虚函数,增强代码可读性与规范性
void display() override { std::cout << "B1::display()" << std::endl; }
};
// 公有继承 B1,D1 是 B1 的派生类,间接继承 B0
class D1 : public B1 {
public:
// override 标记覆盖基类虚函数
void display() override { std::cout << "D1::display()" << std::endl; }
};
// 接收 B0 指针,利用多态调用不同派生类的 display 函数
void fun(B0* ptr) {
// 通过基类指针调用虚函数,运行时会根据实际对象类型动态绑定
ptr->display();
}
int main() {
B0 b0; // 定义 B0 类对象
B1 b1; // 定义 B1 类对象
D1 d1; // 定义 D1 类对象
// 传入不同对象地址,触发多态
fun(&b0); // 调用 B0 的 display
fun(&b1); // 调用 B1 的 display
fun(&d1); // 调用 D1 的 display
return 0;
}
virtual只要在基函数写就行 但推荐在派生类的成员函数也加上virtual
- 普通的函数重载时,其函数的参数或参数类型必须有所不同,函数的返回类型也可以不同。
- 当重载一个虚函数时,也就是说在派生类中重新定义虚函数时,要求函数名、返回类型、参数个数、参数的类型和顺序与基类中的虚函数原型完全相同
基类的析构函数通常需要是虚函数 因为这样便于基类的指针操作
纯虚函数
virtual 函数类型 函数名(参数表)=0;
抽象类 (至少有一个纯虚函数)
if ((p = malloc(n)) == NULL)
/* ... */
if ((p = malloc(n)) == NULL)
/* ... */throw try catch
#include <iostream>
using namespace std;
int main() {
try {
// 抛出一个整数类型异常
throw 10;
// 抛出一个字符串类型异常
throw string("这是一个字符串异常");
}
catch (int e) {
cout << "捕获到整数异常,值为: " << e << endl;
}
catch (...) {
cout << "捕获到其他类型的异常" << endl;
}
return 0;
}
Comments 1 条评论
woc,nubility!