C++ 封装:公共成员与公共方法
假设我有一个名为C++ 封装:公共成员与公共方法,c++,c++14,encapsulation,C++,C++14,Encapsulation,假设我有一个名为PointerSet的类。它本质上是一个向量,通过使用std::lower_bound(但它的工作原理并不重要,因此我将不深入讨论) 模板 类指针集 { ... 公众: 无效插入(T&); 无效擦除(T&); 无效擦除(const std::vector&); }; 拥有“功能隧道”会影响性能吗?(我不知道这个技术术语,但在我的脑海中它就是我所说的。它是一个函数调用一个具有完全相同目的的函数。)请参见版本1 第1版:创建成员方法 ... template<typenam
PointerSet
的类。它本质上是一个向量
,通过使用std::lower_bound
(但它的工作原理并不重要,因此我将不深入讨论)
模板
类指针集
{
...
公众:
无效插入(T&);
无效擦除(T&);
无效擦除(const std::vector&);
};
拥有“功能隧道”会影响性能吗?(我不知道这个技术术语,但在我的脑海中它就是我所说的。它是一个函数调用一个具有完全相同目的的函数。)请参见版本1
第1版:创建成员方法
...
template<typename T>
class Node
{
PointerSet<T> Links;
public:
void insertLink(T& p){ Links.insert(p); }
void eraseLink(T& p){ Links.erase(p); }
void eraseLink(const std::vector<T>& p){ Links.erase(p); }
};
class bar;
class foo : public Node<bar> { }; // now foo can insert links to bars.
...
。。。
模板
类节点
{
指针集链接;
公众:
void insertLink(T&p){Links.insert(p);}
无效擦除链接(T&p){Links.erase(p);}
void eraseLink(const std::vector&p){Links.erase(p);}
};
分类栏;
类foo:公共节点{};//现在,foo可以插入指向条的链接。
...
第2版:只需使用公共成员即可
...
class bar;
struct foo
{
PointerSet<bar> Links; // Use Links directly
};
。。。
分类栏;
结构foo
{
指针集链接;//直接使用链接
};
以下哪种方法是最好的?我考虑的是性能和调试的易用性。除非您想限制可以在该数据成员上执行的操作,否则我建议使用public成员。如果您所做的只是用您自己的方法名包装这些方法,那么将它们放在那里是没有意义的。除非您希望限制可以在该数据成员上执行的操作,否则我建议使用public成员。如果您所做的只是用自己的方法名包装这些方法,那么将它们放在那里是没有意义的。
- 通常,如果IS-A关系不受尊重,您应该避免公共派生,因为它会导致该组合的耦合度更高
- 您还应该避免使用公共成员,因为这会使应用程序在将来更难维护
- 如果您可以在第二个示例中直接使用
,那么在版本1中,您也可以直接从指针集
派生。因此,这种比较是不公平的指针集
- 通常,如果IS-A关系不受尊重,您应该避免公共派生,因为它会导致该组合的耦合度更高
- 您还应该避免使用公共成员,因为这会使应用程序在将来更难维护
- 如果您可以在第二个示例中直接使用
,那么在版本1中,您也可以直接从指针集
派生。因此,这种比较是不公平的指针集
#include <vector>
// these functions are not defined, so that the compiler
// cannot inline them or optimize them out
void insert_impl(void const*);
void erase_impl(void const*);
void erase_impl_vec(void const*);
template<typename T>
class PointerSet
{
public:
void insert(T& v) { insert_impl(&v); }
void erase(T& v) { erase_impl(&v); }
void erase(const std::vector<T>& v) {
erase_impl_vec(&v);
}
};
template<typename T>
class Node
{
PointerSet<T> Links;
public:
void insertLink(T& p){ Links.insert(p); }
void eraseLink(T& p){ Links.erase(p); }
void eraseLink(const std::vector<T>& p){ Links.erase(p); }
};
int main()
{
Node<int> n;
int x;
n.insertLink(x);
n.eraseLink(x);
std::vector<int> v;
n.eraseLink(v);
}
正如您在link()中所看到的,编译器为每个对象输出相同的程序集
main: # @main
push rbx
sub rsp, 48
lea rbx, [rsp + 12]
mov rdi, rbx
call insert_impl(void const*)
mov rdi, rbx
call erase_impl(void const*)
xorps xmm0, xmm0
movaps xmmword ptr [rsp + 16], xmm0
mov qword ptr [rsp + 32], 0
lea rdi, [rsp + 16]
call erase_impl_vec(void const*)
mov rdi, qword ptr [rsp + 16]
test rdi, rdi
je .LBB0_3
call operator delete(void*)
.LBB0_3:
xor eax, eax
add rsp, 48
pop rbx
ret
mov rbx, rax
mov rdi, qword ptr [rsp + 16]
test rdi, rdi
je .LBB0_6
call operator delete(void*)
.LBB0_6:
mov rdi, rbx
call _Unwind_Resume
GCC_except_table0:
.byte 255 # @LPStart Encoding = omit
.byte 3 # @TType Encoding = udata4
.byte 41 # @TType base offset
.byte 3 # Call site Encoding = udata4
.byte 39 # Call site table length
.long .Lfunc_begin0-.Lfunc_begin0 # >> Call Site 1 <<
.long .Ltmp0-.Lfunc_begin0 # Call between .Lfunc_begin0 and .Ltmp0
.long 0 # has no landing pad
.byte 0 # On action: cleanup
.long .Ltmp0-.Lfunc_begin0 # >> Call Site 2 <<
.long .Ltmp1-.Ltmp0 # Call between .Ltmp0 and .Ltmp1
.long .Ltmp2-.Lfunc_begin0 # jumps to .Ltmp2
.byte 0 # On action: cleanup
.long .Ltmp1-.Lfunc_begin0 # >> Call Site 3 <<
.long .Lfunc_end0-.Ltmp1 # Call between .Ltmp1 and .Lfunc_end0
.long 0 # has no landing pad
.byte 0 # On action: cleanup
main:#@main
推送rbx
副区长,48
lea rbx,[rsp+12]
mov rdi,rbx
调用insert_impl(void const*)
mov rdi,rbx
调用擦除\u impl(无效常量*)
xorps xmm0,xmm0
movaps xmmword ptr[rsp+16],xmm0
mov qword ptr[rsp+32],0
李尔迪[rsp+16]
调用擦除执行向量(无效常量*)
mov rdi,qword ptr[rsp+16]
测试rdi,rdi
乙脑LBB0_3
呼叫操作员删除(无效*)
.LBB0_3:
异或eax,eax
加上rsp,48
流行音乐
ret
mov-rbx,rax
mov rdi,qword ptr[rsp+16]
测试rdi,rdi
乙脑LBB0_6
呼叫操作员删除(无效*)
.LBB0_6:
mov rdi,rbx
打电话(解除)(恢复)
GCC_除表0外:
.byte 255#@LPStart Encoding=省略
.byte 3#@t类型编码=udata4
.byte 41#@t类型基偏移量
.byte 3#调用站点编码=udata4
.字节39#调用站点表长度
.long.Lfunc_begin0-.Lfunc_begin0#>>呼叫站点1>呼叫站点2>呼叫站点3
拥有“功能隧道”会影响性能吗
很可能不是。特别是因为您在这里处理的是模板,所以定义都是可见的,并且编译器很容易内联。请查看此链接:
我所做的是编译这两个代码片段。首先,从节点类调用函数
#include <vector>
// these functions are not defined, so that the compiler
// cannot inline them or optimize them out
void insert_impl(void const*);
void erase_impl(void const*);
void erase_impl_vec(void const*);
template<typename T>
class PointerSet
{
public:
void insert(T& v) { insert_impl(&v); }
void erase(T& v) { erase_impl(&v); }
void erase(const std::vector<T>& v) {
erase_impl_vec(&v);
}
};
template<typename T>
class Node
{
PointerSet<T> Links;
public:
void insertLink(T& p){ Links.insert(p); }
void eraseLink(T& p){ Links.erase(p); }
void eraseLink(const std::vector<T>& p){ Links.erase(p); }
};
int main()
{
Node<int> n;
int x;
n.insertLink(x);
n.eraseLink(x);
std::vector<int> v;
n.eraseLink(v);
}
正如您在link()中所看到的,编译器为每个对象输出相同的程序集
main: # @main
push rbx
sub rsp, 48
lea rbx, [rsp + 12]
mov rdi, rbx
call insert_impl(void const*)
mov rdi, rbx
call erase_impl(void const*)
xorps xmm0, xmm0
movaps xmmword ptr [rsp + 16], xmm0
mov qword ptr [rsp + 32], 0
lea rdi, [rsp + 16]
call erase_impl_vec(void const*)
mov rdi, qword ptr [rsp + 16]
test rdi, rdi
je .LBB0_3
call operator delete(void*)
.LBB0_3:
xor eax, eax
add rsp, 48
pop rbx
ret
mov rbx, rax
mov rdi, qword ptr [rsp + 16]
test rdi, rdi
je .LBB0_6
call operator delete(void*)
.LBB0_6:
mov rdi, rbx
call _Unwind_Resume
GCC_except_table0:
.byte 255 # @LPStart Encoding = omit
.byte 3 # @TType Encoding = udata4
.byte 41 # @TType base offset
.byte 3 # Call site Encoding = udata4
.byte 39 # Call site table length
.long .Lfunc_begin0-.Lfunc_begin0 # >> Call Site 1 <<
.long .Ltmp0-.Lfunc_begin0 # Call between .Lfunc_begin0 and .Ltmp0
.long 0 # has no landing pad
.byte 0 # On action: cleanup
.long .Ltmp0-.Lfunc_begin0 # >> Call Site 2 <<
.long .Ltmp1-.Ltmp0 # Call between .Ltmp0 and .Ltmp1
.long .Ltmp2-.Lfunc_begin0 # jumps to .Ltmp2
.byte 0 # On action: cleanup
.long .Ltmp1-.Lfunc_begin0 # >> Call Site 3 <<
.long .Lfunc_end0-.Ltmp1 # Call between .Ltmp1 and .Lfunc_end0
.long 0 # has no landing pad
.byte 0 # On action: cleanup
main:#@main
推送rbx
副区长,48
lea rbx,[rsp+12]
mov rdi,rbx
调用insert_impl(void const*)
mov rdi,rbx
调用擦除\u impl(无效常量*)
xorps xmm0,xmm0
movaps xmmword ptr[rsp+16],xmm0
mov qword ptr[rsp+32],0
李尔迪[rsp+16]
调用擦除执行向量(无效常量*)
mov-rdi,qw