c/c++ 奇技淫巧(一些c语言的技巧)
发布时间 - 2026-01-11 00:09:24 点击率:次一. 变长数组

严格说来,变长数组的实现在c++中并不是一件麻烦的事情。Stl中的vector本身就是一个变长数组,并且有自动管理内存的能力。
但是在c中,实现变长数组就稍显麻烦。用C实现,必然需要一个结构,结构当中应当有一个指针,指针分配一段内存空间,空间大小根据需要而定,而且必须有另外一个字段记录究竟开辟了多大多长的空间。
大致描述如下:
Struct MutableLenArray
{
Int count;
Char* p;
};
P = new Char[Count];
没什么问题,但是C语言的使用者有个最大的自豪就在于对于效率、空间使用的掌控。他们会有这样的疑问,如果count=0,那么p就没必要了,白白占了4(64位系统为8)个字节的空间,简直浪费。
那有没有更好的方式能实现上面的需求,又保证空间合理呢?答案是有的,用0长度
Struct MutableLenArray
{
Int count;
Char p[0];
};
和上面的结构使用方法一致,但是我们可以用sizeof尝试读取其大小,发现竟然只有count字段的长度4字节,p没有被分配空间。完美!
二. 宏的妙用
1. #和
“#”符号把一个符号直接转换为字符串,例如:
#define TO_STRING(x) #x const char *str = TO_STRING( test );
str的内容就是”test “,也就是说#会把其后的符号 直接加上双引号。
这个特性为c++反射的实现提供了极大便利,可以参考博主的下一篇文章,c++反射的简单实现。
##符号会连接两个符号,从而产生新的符号(词法层次),例如:
#define SIGN( x ) INT_##x int SIGN( 1 );
宏被展开后将成为:int INT_1;
可以把##看成连字符,连字符为则为新符号的产生提供了方便。Google的Gtest框架就巧妙的运用了连字符来生成新的测试案例。
2. 变参宏
#define LOG( format, ... ) printf( format, __VA_ARGS__ ) LOG( "%s %d", str, count );
VA_ARGS是系统预定义宏,被自动替换为参数列表。
经常需要进行输出格式化,重定义时,可以用到以上技巧。
3. 宏参数的prescan
prescan的定义:当一个宏参数被放进宏体时,这个宏参数会首先被全部展开(有例外,见下文)。当展开后的宏参数被放进宏体时, 预处理器对新展开的宏体进行第二次扫描,并继续展开。例如:
#define PARAM( x ) x #define ADDPARAM( x ) INT_##x PARAM( ADDPARAM( 1 ) );
因为ADDPARAM( 1 ) 是作为PARAM的宏参数,所以先将ADDPARAM( 1 )展开为INT_1,然后再将INT_1放进PARAM。
例外情况是,如果PARAM宏里对宏参数使用了#或##,那么宏参数不会被展开:
#define PARAM( x ) #x #define ADDPARAM( x ) INT_##x
PARAM( ADDPARAM( 1 ) ); 将被展开为”ADDPARAM( 1 )”。
所以此时要得到“INT_1”的结果,必须加入一个中间宏:
#define PARAM(x) PARAM1(x) #define PARAM1( x ) #x
PARAM( ADDPARAM( 1 ) );此时的结果将会是“INT_1”。根据prescan原则,当ADDPARAM(1)传入,会展开得到INT_1,然后将INT_1带入PARAM1宏,最终得到“INT_1”的结果。
4. 接口宏
以下部分,摘自网上博客,仅作声明。
C++的目标之一就是把类的声明和定义分离开来,这对于项目的开发极其有利——这可以使开发人员不用看到类的实现就能知晓类的功能。但是,C++实现类的声明与类定义的分离的方法会导致一些额外的工作——每个非内联函数的表示都需要写两次,一次在类声明中,一次在类定义中。
代码如下:
// .h File
class Element
{
void Tick ();
};
// .cpp File
void Element ::Tick ()
{
// todo
}
由于Tick的标识在两个地方都出现了,因此如果我们需要改变这个方法的参数的时候(改变函数名、返回类型或者加const),我们需要改变两个地方。
当然通常这没有什么工作量,但是有些情况下这个特性会带来不少麻烦。
举个例子,如果我们有一个叫做BaseClass的基类,有三个从BaseClass继承而来的子类——D1、D2和D3.其中BaseClass声明了一个虚函数Foo()并且有一个缺省实现,并且D1、D2、D3中重载了Foo()函数。
现在,如果说我们给BaseClass::Foo()添加一个参数,但是忘了给D3中做相应的修改。麻烦来了——编译可以通过,编译器会把BaseClass::Foo(…)和D3::Foo()当成两个完全不同的函数。当我们想通过虚函数机制来调用D3的Foo的时候,这就容易出一些问题。
UE4中光继承自AActor类的类就有上千个,如果需要对AActor类做一个修改,那么如果使用传统方法,我们还要针对上千个派生类进行修改,而且万一有一个派生类没有修改,编译器也不会报错!
这么看来,理想的情况是我们希望一个函数的表示只在一个地方存在,如果说只声明BaseClass::Foo()一次,然后再它的派生类中不用再额外声明Foo就好了。
而且在效率方面来说,在C++中使用继承的时候我们经常会使用很多浅层次的类继承关系,一个父类往往有一堆子类。很多时候我们只需要把很多互不相关的功能集成到一个单独的类继承家族里面。
对于浅继承来说,我们只是把开始的父类声明为一个接口——也就是说它声明了一些虚函数(大部分是纯虚函数)。在大多数情况下,我们会在这个类家族里面有一个基类以及其余的派生类。
如果说我们的基类有10个函数,我们从这个基类派生了20个类,那么我们就需要额外做200个函数声明。但是这些声明的目的往往只是为了Implement基类中的那些方法而已,这就或多或少的容易使得头文件不好维护。
传统方法的实现
如果说我们有一个Animal的类,这个类被视为基类,我们希望从这个基类派生出不同的子类。在Animal中有3个纯需函数,如下所示:
class Animal
{
public:
virtual std :: string GetName () const = 0 ;
virtual Vector3f GetPosition () const = 0;
virtual Vector3f GetVelocity () const = 0;
};
同时,这个基类拥有三个派生类——Monkey,Tiger,Lion。
那么我们三个方法的每一个都会在7个地方存在:Animal中一次,Monkey、Lion、Tiget的声明和定义各一次。
然后假设我们做一个小改动——我们想将GetPosition和GetVelocity的返回类型改为Vector4f以适应Transform变换,那么我们就要在7个地方进行修改:Animal的.h文件,Lion、Tiger和Monkey的.h文件和.cpp文件。
使用宏的实现
有一种很妙的处理方法就是将这些方法进行包装,改成所谓接口宏的形式。我们可以试试看:
#define INTERFACE_ANIMAL(terminal) \ public: \ virtual std::string GetName() const ##terminal \ virtual IntVector GetPosition() const ##terminal \ virtual IntVector GetVelocity() const ##terminal #define BASE_ANIMAL INTERFACE_ANIMAL(=0;) #define DERIVED_ANIMAL INTERFACE_ANIMAL(;)
值得一提的是,##符号代表的是连接,\符号代表的是把下一行的连起来。
通过这些宏,我们就可以大大简化Animal的声明,还有所有从它派生的类的声明了:
// Animal.h
class Animal
{
BASE_ANIMAL ;
};
// Monkey.h
class Monkey : public Animal
{
DERIVED_ANIMAL ;
};
// Lion.h
class Lion : public Animal
{
DERIVED_ANIMAL ;
};
// Tiger.h
class Tiger : public Animal
{
DERIVED_ANIMAL ;
};
# c++
# 奇技淫巧
# C语言算法积累分离数位示例
# c语言分离三位数的实现
# 有一个
# 的是
# 如果说
# 子类
# 变长
# 派生类
# 会在
# 这就
# 做一个
# 会把
# 类中
# 有什么
# 也不
# 来了
# 会有
# 有个
# 就能
# 将会
# 就有
# 有一种
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
如何确保FTP站点访问权限与数据传输安全?
创业网站制作流程,创业网站可靠吗?
Laravel怎么使用Intervention Image库处理图片上传和缩放
如何在IIS7上新建站点并设置安全权限?
Laravel Debugbar怎么安装_Laravel调试工具栏配置指南
Laravel如何使用Gate和Policy进行权限控制_Laravel权限判定与策略规则配置
猪八戒网站制作视频,开发一个猪八戒网站,大约需要多少?或者自己请程序员,需要什么程序员,多少程序员能完成?
Android okhttputils现在进度显示实例代码
阿里云高弹*务器配置方案|支持分布式架构与多节点部署
WEB开发之注册页面验证码倒计时代码的实现
如何快速搭建高效WAP手机网站?
免费网站制作appp,免费制作app哪个平台好?
网站建设整体流程解析,建站其实很容易!
详解Android图表 MPAndroidChart折线图
Windows10如何更改计算机工作组_Win10系统属性修改Workgroup
详解阿里云nginx服务器多站点的配置
专业型网站制作公司有哪些,我设计专业的,谁给推荐几个设计师兼职类的网站?
Laravel如何实现多表关联模型定义_Laravel多对多关系及中间表数据存取【方法】
Laravel中Service Container是做什么的_Laravel服务容器与依赖注入核心概念解析
zabbix利用python脚本发送报警邮件的方法
详解jQuery中的事件
Laravel如何使用Eloquent进行子查询
想要更高端的建设网站,这些原则一定要坚持!
米侠浏览器网页图片不显示怎么办 米侠图片加载修复
Laravel怎么实现模型属性的自动加密
Laravel怎么连接多个数据库_Laravel多数据库连接配置
原生JS获取元素集合的子元素宽度实例
Laravel如何使用Socialite实现第三方登录?(微信/GitHub示例)
韩国代理服务器如何选?解析IP设置技巧与跨境访问优化指南
Laravel如何升级到最新的版本_Laravel版本升级流程与兼容性处理
什么是javascript作用域_全局和局部作用域有什么区别?
C语言设计一个闪闪的圣诞树
高端云建站费用究竟需要多少预算?
佛山企业网站制作公司有哪些,沟通100网上服务官网?
Laravel如何实现RSS订阅源功能_Laravel动态生成网站XML格式订阅内容【教程】
Laravel项目怎么部署到Linux_Laravel Nginx配置详解
如何在Windows服务器上快速搭建网站?
Laravel怎么定时执行任务_Laravel任务调度器Schedule配置与Cron设置【教程】
rsync同步时出现rsync: failed to set times on “xxxx”: Operation not permitted
打开php文件提示内存不足_怎么调整php内存限制【解决方案】
ChatGPT常用指令模板大全 新手快速上手的万能Prompt合集
网站建设要注意的标准 促进网站用户好感度!
Android中Textview和图片同行显示(文字超出用省略号,图片自动靠右边)
Win11怎样安装网易有道词典_Win11安装词典教程【步骤】
瓜子二手车官方网站在线入口 瓜子二手车网页版官网通道入口
装修招标网站设计制作流程,装修招标流程?
如何为不同团队 ID 动态生成多个独立按钮
ChatGPT回答中断怎么办 引导AI继续输出完整内容的方法
html5的keygen标签为什么废弃_替代方案说明【解答】
弹幕视频网站制作教程下载,弹幕视频网站是什么意思?

