我刚才读到我们不应该过度使用虚函数.人们认为虚拟功能越少,错误越少,维护也越少.
由于虚函数会出现什么样的错误和缺点?
我对C++或Java的上下文感兴趣.
我能想到的一个原因是由于v表查找,虚函数可能比正常函数慢.
我在阅读c ++中的虚拟继承时在网站上发现了这个
使用多重继承时,有时需要使用虚拟继承.一个很好的例子是标准的iostream类层次结构:
//Note: this is a simplified description of iostream classes
class ostream: virtual public ios { /*..*/ }
class istream: virtual public ios { /*..*/ }
class iostream : public istream, public ostream { /*..*/ }
//a single ios inherited
Run Code Online (Sandbox Code Playgroud)
C++如何确保只存在虚拟成员的单个实例,而不管从中派生的类的数量是多少?C++使用额外的间接级别来访问虚拟类,通常是通过指针.换句话说,iostream层次结构中的每个对象都有一个指向ios对象的共享实例的指针.额外的间接级别有轻微的性能开销,但这是一个很小的代价.
我对声明感到困惑:
C++使用额外的间接级别来访问虚拟类,通常是通过指针
任何人都能解释一下吗?
c++ inheritance multiple-inheritance virtual-inheritance object-layout
在的C++ 0x草案有围栏的概念似乎很明显,从围墙的CPU /芯片级的概念,或者说什么linux内核的家伙想到的围栏.问题在于草案是否真的意味着一个极其受限制的模型,或者措辞是否很差,它实际上意味着真正的围栏.
例如,在29.8 Fences下它表示如下:
如果存在原子操作X和Y,则释放围栏A与获取围栏B同步,两者都在某个原子对象M上操作,使得A在X之前被排序,X修改M,Y在B之前被排序,并且Y读取该值如果是释放操作,则由X写入或由假设释放序列中的任何一方写入的值X将结束.
它使用这些术语atomic operations和atomic object.草案中定义了这样的原子操作和方法,但它仅仅意味着那些吗?一个释放栅栏听起来像一个店围栏.在围栏之前不保证写入所有数据的商店围栏几乎是无用的.类似于装载(获取)围栏和完整围栏.
那么,C++ 0x中的栅栏/栅栏是否适当的栅栏和措辞是否非常差,或者它们是否如所描述的那样极其受限制/无用?
就C++而言,假设我有这个现有的代码(假设现在可以使用围栏作为高级构造 - 而不是在GCC中使用__sync_synchronize):
Thread A:
b = 9;
store_fence();
a = 5;
Thread B:
if( a == 5 )
{
load_fence();
c = b;
}
Run Code Online (Sandbox Code Playgroud)
假设a,b,c的大小在平台上具有原子拷贝.以上意味着c只会被分配9.注意我们并不关心线程B何时看到a==5,只是当它看到它时b==9.
C++ 0x中保证相同关系的代码是什么?
答案:如果您阅读我选择的答案和所有评论,您将获得情况的要点.C++ 0x似乎强制您使用带栅栏的原子,而普通硬件栅栏没有此要求.在许多情况下,只要sizeof(atomic<T>) == sizeof(T)和,这仍然可以用来代替并发算法atomic<T>.is_lock_free() == true.
不幸的是,这is_lock_free …
给定以下代码(没有虚拟继承):
class A
{
public:
virtual void f() = 0;
};
class B : public A
{
public:
virtual void f() {}
};
class C : public A
{
public:
virtual void f() {}
};
class D : public B, public C
{
/* some code */
};
int main()
{
D d;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
代码编译.
另一方面,这里:
class A
{
public:
virtual void f() = 0;
};
class B : virtual public A
{
virtual void f() {}
}; …Run Code Online (Sandbox Code Playgroud) c++ virtual overriding multiple-inheritance virtual-inheritance
我正在实现一些C++静态分析规则,其中一个规则禁止函数返回引用或指向函数引用参数的指针,即以下都是不兼容的:
int *f(int& x) { return &x; } // #1
const int *g(const int& x) { return &x; } // #2
int& h(int& x) { return x; } // #3
const int& m(const int& x) { return x; } // #4
Run Code Online (Sandbox Code Playgroud)
为此给出的理由是"它是实现定义的行为,无论引用参数是临时对象还是对参数的引用."
然而,我对此感到困惑,因为C++中的流操作符是以这种方式编写的,例如
std::ostream& operator<<(std::ostream& os, const X& x) {
//...
return os;
}
Run Code Online (Sandbox Code Playgroud)
我认为我非常有信心C++中的流操作符通常不会表现出实现定义的行为,所以发生了什么?
根据我目前的理解,我希望#1和#3能够很好地定义,因为temporaries不能绑定到非const引用,所以int& x指的是一个超出范围的实际对象函数,因此返回指向该对象的指针或引用是好的.我希望#2变得狡猾,因为一个临时的本来可以被束缚const int& x,在这种情况下,试图取其地址似乎是一个糟糕的计划.我不确定#4 - 我的直觉是,这也可能是狡猾的,但我不确定.特别是,我不清楚以下情况会发生什么:
const int& m(const int& x) { return x; }
//...
const int& r = m(23);
Run Code Online (Sandbox Code Playgroud) 据我所知,编译器从不优化声明为的变量volatile.但是,我有一个像这样声明的数组.
volatile long array[8];
Run Code Online (Sandbox Code Playgroud)
不同的线程读写它.数组的元素仅由其中一个线程修改,并由任何其他线程读取.但是,在某些情况下,我注意到即使我从一个线程修改一个元素,读取它的线程也不会注意到这个变化.它继续读取相同的旧值,就好像编译器已将其缓存在某处.但是编译器本身不应该缓存volatile变量,对吧?那怎么会发生这种情况.
注意:我不是volatile用于线程同步,所以请停止给我答案,如使用锁或原子变量.我知道volatile,atomic变量和互斥量之间的区别.另请注意,该体系结构是x86,具有主动缓存一致性.在我认为变量被其他线程修改后,我也读了很长时间.即使经过很长一段时间,阅读线程也看不到修改后的值.
C++内存分配运算符的形式为operator new (size_t s).当我new为类型的类对象重载运算符时T,它是否保证size_t s运算符的输入参数(即)new确切sizeof(T)?如果是,为什么这个函数仍然需要大小作为输入参数?
来自另一个问题:
由于C++ 17,auto x0{1, 2, 3, 4};以前推断出初始化列表,不再允许(当然,我们可以使用auto x0 = {1, 2, 3, 4};...).现在一如既往地避免统一初始化(例如std::vector<int> v({1, 2, 3, 4});,使用初始化列表作为参数进行显式构造函数调用),并且类似于定义良好auto x(7);(我不会自己使用的构造......),我想出了以下内容:
auto x({1, 2, 3, 4});
// -> std::initializer_list<int> x({1, 2, 3, 4});
Run Code Online (Sandbox Code Playgroud)
这是用GCC 7.2.0(mingw64)编译的,但是发出警告(而评论版再次没有):
list-initializer for non-class type must not be parenthesized
我在标准中找不到任何相关内容,所以现在的问题是(出于纯粹的兴趣......):
为什么不允许这样做?(这是否被标准所涵盖,或者我们是否需要将此视为GCC错误?)
通过C++ 17标准阅读,在我看来,pp-number预处理器和数字文字处理之间存在不一致,例如user-defined-integer-literal,它们被定义为由"上层"语言处理.
例如,pp-number根据预处理器语法正确解析以下内容:
123_e+1
Run Code Online (Sandbox Code Playgroud)
但放在符合C++ 11的代码片段的上下文中,
int operator"" _e(unsigned long long)
{ return 0; }
int test()
{
return 123_e+1;
}
Run Code Online (Sandbox Code Playgroud)
目前的Clang或GCC编译器(我还没有测试过其他编译器)会返回类似这样的错误:
unable to find numeric literal operator 'operator""_e+1'
Run Code Online (Sandbox Code Playgroud)
在那里operator"" _e(...)没有找到,并试图定义operator"" _e+1(...)将是无效的.
似乎这是因为编译器将令牌作为pp-number第一个使用,但是user-defined-integer-literal在解析最终表达式时无法回滚并应用语法规则.
相比之下,以下代码编译正常:
int operator"" _d(unsigned long long)
{ return 0; }
int test()
{
return 0x123_d+1; // doesn't lex as a 'pp-number' because 'sign' can only follow [eEpP]
}
Run Code Online (Sandbox Code Playgroud)
这是标准的正确读数吗?如果是这样,编译器应该处理这个,可以说是罕见的极端案例是否合理?
c++ syntax language-lawyer user-defined-literals c-preprocessor
看着经过一大堆 的 其他 问题 和 他们的 答案,我得到的印象是有什么在C“挥发性”关键字表示正好没有广泛的协议。
即使标准本身似乎也不够清晰,以至于每个人都无法理解其含义。
除其他问题外:
总结一下问题,似乎(经过大量阅读)“ volatile”保证了类似的结果:该值将不但从/向寄存器,而且至少向内核的L1缓存中读/写,其顺序与读/写出现在代码中。但这似乎没有用,因为在同一线程内从寄存器中读取/写入寄存器已经足够,而与L1缓存进行协调并不能保证与其他线程进行协调。我无法想象仅与L1缓存进行同步的重要性。
用途1
唯一广泛同意使用volatile的似乎是旧的或嵌入式系统,其中某些内存位置通过硬件映射到I / O功能,例如内存中的某个位(直接在硬件中)控制灯光。 ,或内存中的某个位告诉您键盘键是否按下(因为它是通过硬件直接连接到键的)。
看来,“用1”不移植的代码,其目标包括多核系统发生。
USE 2
与“ use 1”没什么不同,是可由中断处理程序(可以控制灯光或存储来自按键的信息)随时读取或写入的内存。但是为此已经存在一个问题,即取决于系统,中断处理程序可能会在 具有自己的内存缓存的不同内核上运行,并且“ volatile”不能保证所有系统上的缓存一致性。
因此,“使用2”似乎超出了“易失性”所能提供的范围。
用途3
我看到的唯一其他无可争议的用途是防止通过指向编译器未意识到的同一内存的不同变量的不同变量对访问进行错误优化。但这可能只是无可争议的,因为人们没有在谈论它-我只看到其中一个提及。而且我认为C标准已经认识到“不同”的指针(例如指向函数的不同args)可能指向同一项目或附近的项目,并且已经指定编译器必须生成即使在这种情况下也可以工作的代码。但是,我无法在最新的标准(500页!)中快速找到此主题。
那么“使用3”也许根本不存在?
因此,我的问题是:
在多核系统的可移植C代码中,“ volatile”是否可以保证任何东西?
浏览最新标准后,答案似乎至少是非常有限的:
1.该标准针对特定类型“ volatile sig_atomic_t”反复指定特殊处理。但是该标准还说,在多线程程序中使用信号功能会导致不确定的行为。因此,该用例似乎仅限于单线程程序与其信号处理程序之间的通信。
2.该标准还为setjmp / longjmp指定了“ volatile”的明确含义。(在其他问题和答案中给出了重要示例代码)。
因此,更精确的问题变成了:
除了(1)允许单线程程序从其信号处理程序接收信息之外,还是(2)允许setjmp,“ volatile”对于多核系统的便携式C代码是否有任何保证?代码以查看在setjmp和longjmp之间修改的变量?
这仍然是一个是/否问题。
如果为“是”,那么最好显示一个无错误的可移植代码示例,如果省略了“ volatile”,则该示例会出现错误。如果为“ no”,那么我认为对于多核目标,在这两种非常特殊的情况下,编译器可以随意忽略“ volatile”。
c++ ×9
c ×2
volatile ×2
c++11 ×1
c++17 ×1
inheritance ×1
initializer ×1
java ×1
memory-model ×1
new-operator ×1
overriding ×1
portability ×1
reference ×1
syntax ×1
virtual ×1
x86 ×1